Как писать квайны

    Введение

    Многие программисты считают написание квайнов (программ, выводящих свой исходный код) непосильной задачей. И действительно — все эти цепные квайны и квайны различного порядка, при взгляде на которые можно потеряться в, казалось бы, бессмысленном наборе символов…

    Однако, на самом деле, написать квайн на каком-либо языке не так сложно, как кажется. Сейчас я расскажу, как сделать это на различных языках программирования. Более того, мы не будем использовать «хаки» интерпретеруемых языков вроде операции вывода исходного кода и функций типа eval и напишем квайны на интерпретируемых и компилируемых языках.

    Теория

    Попробуем написать квайн. Для этого возьмём инструкцию языка для вывода и передадим ей как параметр код программы. Однако, в коде мы снова используем этот же код и так далее — возникает бесконечная рекурсия. Но что можно сделать для того, чтобы не передавать строковую константу? Решение — поместить строку (копию части кода) в переменную. Для удобства назовём такую строку s-строкой, а переменную с этой строкой — s-переменной. Чтобы и в s-переменной не было рекурсии, мы просто исключим из неё фрагмент со значением этой самой переменной. То есть, выглядеть это будет примерно так:

    C:
    char s[]="char s[]=;";

    Примечание. Как в этом фрагменте, так и в квайнах, которые получатся в итоге, для простоты мы не будем соблюдать правила форматирования кода. Тем не менее, их соблюдение вы сможете без труда добавить самостоятельно после прочтения статьи.

    Далее, при выводе, мы подставим значение s-строки в её же определение в коде (в примере выше — перед тремя последними символами). Здесь же возникает ещё несколько проблем. Первая проблема — при подставлении в s-строке нельзя использовать символы, которые поведут себя в коде не так, как надо. Например, мы не можем так просто вставить кавычку — ведь вместо того, чтобы стать частью s-строки, она завершит её определение и выводимый код не будет совпадать с исходным, являясь некорректным вообще.

    Экранирование применить здесь достаточно сложно — символ экранирования надо тоже экранировать и т.д.. Гораздо проще, например, использовать другой вариант кавычек — так, во многих интерпретируемых языках разрешено использование как одинарных, так и двойных кавычек для задания строки, а отличие состоит в том, что можно без проблем использовать одну кавычку в константе, если она ограничивается другими. То есть, код '"' создаст односимвольную строку с двойной кавычкой, а код "'" — с одиночной. Если использовать этот вариант, удобно задать в начале переменную с какой-либо кавычкой, а затем использовать её при выводе.

    Но и этот вариант не универсален: в Си, к примеру, есть лишь один вариант кавычек. Тогда можно использовать другой способ — задавать кавычку кодом символа, печатая символ с таким кодом при выводе.

    Следующая проблема — вставка другой строки (или символа с каким-либо кодом) в вывод s-строки. Решение здесь очевидно — брать подстроку s-строки специальной функцией, выводить её, далее выводить то, что надо вставить, затем выводить другую подстроку s-строки. Может показаться, что в Си взятие подстроки для вывода потребует немало кода. Тут нам на помощь придёт мощь функции printf. Так, например, вот варианты кода для различных языков, печатающего часть строки s со второго символа (считая с единицы) по четвёртый включительно:

    Python:
    print(s[1:4])

    Ruby:
    print s[1..3]

    Perl:
    print substr(s,1,2)

    C:
    printf("%.2s",s+1);

    Обычно методы взятия подстроки могут также брать её остаток до конца. Например, напечатаем строку s со второго символа до конца строки (то есть, всю строку кроме первого символа):

    Python:
    print(s[1:])

    Ruby:
    print s[1..-1]

    Perl:
    print substr(s,1)

    C:
    printf("%s",s+1);

    Если такой возможности нет, придётся на место параметра с длиной подстроки поставить заглушку типа «XX», а затем в конце посчитать символы до конца и подставить их вместо «XX» в коде и в s-строке, не изменяя длины различных частей кода. Например, если в длине окажется одна цифра, целесообразно подставить вместо первого икса пробел, ведь, если его удалить, длины частей s-строки изменятся и их придётся пересчитывать.

    Интерпретируемые языки

    Итак, начнём писать квайны, собрав все суждения выше. На Python я написал такой квайн (работает и на 3.x):
    q="'";s='q="";s=;print(s[:3]+q+s[3:7]+q+s+q+s[7:])';print(s[:3]+q+s[3:7]+q+s+q+s[7:])

    Здесь переменная q используется как переменная, где хранится одинарная кавычка, далее идёт определение s-переменной со всем кодом, кроме самой s-строки. После этого идёт вывод s-переменной со следующими вставками:
    1). Одинарная кавычка как значение переменной q;
    2). Одинарная кавычка как начало определения s-строки;
    3). Сама s-строка (да-да, s-строка вставляется внутри s-строки);
    4). Одинарная кавычка как конец определения s-строки.

    Примечание. При написании квайнов по данному методу не забывайте копировать все изменения в коде в копию кода в s-строке.

    С минимальными изменениями можно получить квайн только для Python 2.x:
    q="'";s='q="";s=;print s[:3]+q+s[3:7]+q+s+q+s[7:]';print s[:3]+q+s[3:7]+q+s+q+s[7:]

    Абсолютно аналогичны и квайны на других языках, где мы изменяем лишь некоторые синтаксические особенности:

    Ruby:
    q="'";s='q="";s=;print s[0..2]+q+s[3..6]+q+s+q+s[7..-1]';print s[0..2]+q+s[3..6]+q+s+q+s[7..-1]

    Perl:
    $q="'";$s='$q="";$s=;print substr($s,0,4).$q.substr($s,4,5).$q.$s.$q.substr($s,9)';print substr($s,0,4).$q.substr($s,4,5).$q.$s.$q.substr($s,9)

    PHP:
    <?$q="'";$s='<?$q="";$s=;print substr($s,0,6).$q.substr($s,6,5).$q.$s.$q.substr($s,11);';print substr($s,0,6).$q.substr($s,6,5).$q.$s.$q.substr($s,11);

    Компилируемые языки.

    Написание квайна на C оказалось чуть более трудной задачей. Здесь я активно использовал коды символов: двойной кавычки — 34, и перевода строки — 13 (он понадобился, чтобы отделить директиву компилятора для включения stdio.h), а также интересный способ взятия подстроки с помощью printf, уже описанный выше.

    А вот и сам квайн:
    #include <stdio.h>
    int main(){const char *s="#include <stdio.h>int main(){const char *s=;printf(%.18s%c%.25s%c%s%c%.8s%c%.33s%c%s,s,10,s+18,34,s,34,s+43,34,s+51,34,s+84);return 0;}";printf("%.18s%c%.25s%c%s%c%.8s%c%.33s%c%s",s,10,s+18,34,s,34,s+43,34,s+51,34,s+84);return 0;}

    Заключение

    Вот и всё. Я написал квайны на большинстве языков, интерпретаторы и компиляторы которых обнаружил на своём компьютере. Думаю, теперь вы и сами напишете подобную программу на своём любимом языке программирования, если я не упомянул его здесь. В качестве упражнения вы также можете написать квайн на таких языках, как Java, C#, Haskell или Pascal. Не бойтесь трудностей — достаточно попробовать, и всё получится!
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 56

      –7
      Квайн на Basic:

      LIST
        +7
        Вы, по-видимому, не читали данную в статье ссылку. Там прямо в первом абзаце сказано, что LIST — является грязным хаком.
          0
          некоторым людям нравятся грязные хаки
        +4
        HQ9+
        Q
          +8
          Можно еще на 1 байт сократить размер программы. Тогда будет работать на большем количестве языков, а не только на HQ9+. :-)
            +5
            Вырождение программы до нулевого размера также не является квайном
              0
              Только после этого случая: www0.us.ioccc.org/1994/smr.hint

              In the future, the contest rules will specify a minimum size that is one character larger than this entry, forever eliminating this sort of program from contest. After all, how many variations can one make on this entry? :-)
          –2
          хм, а к чему это? Я понимаю так — если бы выходные данные можно было подать в качестве входных данных, модифицировать, перекомпилировать, а за тем снова использовать в качестве выходного параметра для самой себя было намного интереснее. Избавляться от рекурсии тут совершенно зря. Рекурсия нужна, встает только вопрос что лучше использовать в качестве выходной стоки для разбора, исходный язык программирования, бинарную строку или нечто промежуточное, типа байт кода.
            0
            Все таки, как правильно, квайн или куайн
              +1
              квайн
                0
                квайн
                  0
                  Оба варианта допустимы.

                  > Куайн, квайн (англ. quine) — компьютерная программа, которая выдаёт на выходе точную копию своего исходного текста. Следует заметить, что программы, использующие внешние данные, куайнами не считаются; то есть исключается прочтение текста программы из файла, ввод его с клавиатуры и так далее. Кроме того, не считается куайном «программа», не содержащая вообще никакого кода (вырожденный случай). (с) Wikipedia
                    +16
                    Кавайн ^_^
                      +1
                      кўайн :)
                      +1
                      Попробуем написать квайн. Для этого возьмём инструкцию языка для вывода и передадим ей как параметр код программы. Однако в коде мы снова используем этот же код и так далее — возникает бесконечная рекурсия. Но что можно сделать для того, чтобы не передавать строковую константу? Решение — поместить строку (копию части кода) в переменную. Для удобства назовём такую строку s-строкой, а переменную с этой строкой — s-переменной. Чтобы и в s-переменной не было рекурсии, мы просто исключим из неё фрагмент задания этой самой переменной. То есть, выглядит это примерно так:

                      C:
                      const char *s=«const char *s=;»;


                      Вы решили взорвать мой мозг, чтобы в понедельник я на работу не вышел? ) Интересная ПЕРЕМЕНная с модификатором const, причем не содержащая «фрагмент задания этой самой переменной» «const char *s=;»
                        +1
                        Поправил)
                          +1
                          Ухты, спасибо =) В принципе мысль и так была понятна, просто спросонья решил побурчать…
                        –2
                        Наверно самый простой квайн это в бинарнике (если считать что бинарник это самый машинный язык), открыть свой исполняемый на чтение и вывести в хексах или в бине.
                          0
                          > Куайн, квайн (англ. quine) — компьютерная программа (частный случай метапрограммирования), которая выдаёт на выходе точную копию своего исходного текста. Следует заметить, что программы, использующие внешние данные, куайнами не считаются; то есть исключается прочтение текста программы из файла, ввод его с клавиатуры и так далее. Кроме того, не считается куайном «программа», не содержащая вообще никакого кода (вырожденный случай).
                          +8
                          Хороший квайн на питоне нашёл. Сохранить в файл fail.py и запустить:

                          File «fail.py», line 1
                          File «fail.py», line 1
                          ^
                          IndentationError: unexpected indent

                          0
                          Квайн на Perl:
                          seek( DATA, 0, 0 );
                          print ;
                          __DATA__
                            –1
                            Вернее
                            seek( DATA, 0, 0 );
                            print <DATA>;
                            __DATA__;
                              +2
                              Еще проще: open 0; print <0>;
                            0
                            Более того, мы не будем использовать «хаки» интерпретеруемых языков вроде операции вывода исходного кода и функций типа eval

                            Простите, а можно поподробнее про вывод кода программы с помощью операции вывода кода программы или функции eval()? На питоне, например.
                            Даже не подозревал о таком.
                              0
                              Конечно, сразу код так не вывести, но часто это может значительно упростить задачу: только лишь присвоили переменной код, вывели код и исполнили код.
                                0
                                Понятно. спасибо.
                              +3
                              JavaScript (Mozilla JaggerMonkey) с выводом в консоль:
                              (function quine() {
                                  console.log("(" + quine.toString() + "())");
                              }())
                              

                              Т.к. движков JavaScript много, то универсальный квайн написать нельзя
                                +1
                                Думаю это тоже читерство, но вот более переносимая версия, правда для другого языка. :] Если сделать (setf *print-pretty* nil), то любая реализация должна вывести одинаково:

                                (DEFUN QUINE NIL (WRITE (APPEND (QUOTE (DEFUN QUINE NIL)) (CDDR (THIRD (FUNCTION-LAMBDA-EXPRESSION (FUNCTION QUINE)))))) (WRITE (QUOTE (QUINE))))(QUINE)

                                Вот версия с pretty print'ом, но её разные реализации могут с отличиями выводить:

                                (DEFUN QUINE ()
                                  (WRITE
                                    (APPEND '(DEFUN QUINE ())
                                                    (CDDR (THIRD (FUNCTION-LAMBDA-EXPRESSION #'QUINE)))))
                                  (WRITE '(QUINE)))(QUINE)
                                  0
                                  Можно короче

                                  (function () {
                                      console.log("(" + arguments.calee.toString() + "())");
                                  }())
                                  
                                  +1
                                  Без строк. :P

                                  (=> — это вывод системы)

                                  (progn (defparameter s '(progn (defparameter s) (write (list (first s) (append (second s) (list `',s)) (third s))))) (write (list (first s) (append (second s) (list `',s)) (third s))))

                                  => (progn (defparameter s '(progn (defparameter s) (write (list (first s) (append (second s) (list `',s)) (third s))))) (write (list (first s) (append (second s) (list `',s)) (third s))))

                                  (eval *) ; Попробуем выполнить результат

                                  => (progn (defparameter s '(progn (defparameter s) (write (list (first s) (append (second s) (list `',s)) (third s))))) (write (list (first s) (append (second s) (list `',s)) (third s))))

                                  (equalp ** ++) ; равна ли позапрошлая команда тому что она выводит

                                  => t               ; true :)
                                    –1
                                    Python-читерство =))

                                    print(open(__file__).read())
                                      +2
                                      #!/bin/cat
                                        0
                                        Штуки как <?php echo file_get_contents(__FILE__); ?> квайнами не сичтаются: «исключается прочтение текста программы из файла» — из википедии.
                                          0
                                          Мне просто надоело уже википедию копировать, ведь не первый раз уже.
                                        0
                                        Какое практическое применение квайнов? Тема интересная, но пока не могу понять где в практике можно использовать.
                                          +2
                                          нигде
                                            +1
                                            По оценкам за мои два комментария в этом топике можно подумать что капитанство оценивается на Хабре выше чем написание квайнов.
                                              –1
                                              Хабр такой хабр.
                                            +2
                                            Также как и решение задач, головоломок ребусов и тд. в реальной жизни мало где находят применения, однако ж действия сии заставляют мозги работать и держат их в тонусе. Так что польза от этого все же есть.
                                              –2
                                              На собеседовании могут спросить
                                              0
                                              <?php printf($a='<?php printf($a=%c%s%c,39,$a,39);',39,$a,39);
                                              
                                                0
                                                А порядок вычисления параметров фукции гарантируется? Точно ли присваивание выполнится до использования $a в качестве третьего параметра? И не надо ли закрыть скобку?
                                                  0
                                                  гарантируется. точно. какую?
                                                    0
                                                    Парную к <?php? Или она и не должна закрываться?
                                                0
                                                На windows cmd считается за квайн?
                                                <---cut--->
                                                @type %0
                                                <---cut--->
                                                Или это тоже грязный хак?
                                                  0
                                                  Скорей всего хак. Т.к. явно читает код собственной программы.
                                                  0
                                                  main = putStr (s ++ show s) where s = «main = putStr (s ++ show s) where s = „
                                                    +1
                                                    чисто для отчетности

                                                    Java

                                                    package ru.fso.test;public class Quine {public static void main(String[] args) {char sq = '"';String cf = "%c";String sf = "%s";String s = "package ru.fso.test;public class Quine {public static void main(String[] args) {char sq = '%c';String cf = %c%s%c;String sf = %c%s%c;String s = %c%s%c;System.out.println(String.format(s,sq,sq,cf,sq,sq,sf,sq,sq,s,sq));}}";System.out.println(String.format(s,sq,sq,cf,sq,sq,sf,sq,sq,s,sq));}}
                                                      0
                                                      А квайн не лиспе:

                                                      T

                                                      ?
                                                        0
                                                        А я ещё стопицот таких «квайнов» на лиспе знаю:

                                                        «a»
                                                        «aa»
                                                        «aaa»
                                                        «100500»
                                                        и т.д.

                                                        Впрочем, они и в хаскеле, и в руби, и в питоне работают.

                                                        Квайн должен явно распечатывать свой исходный код. Если выполнить ваш квайн, сохранив в файл, то ничего не выведется.
                                                        0
                                                        <?php
                                                        header("Content-type: text/plain; charset=UTF-8");
                                                        function hexToStr($hex)
                                                        {
                                                        $string='';
                                                        for ($i=0; $i < strlen($hex)-1; $i+=2)
                                                        {
                                                        $string .= chr(hexdec($hex[$i].$hex[$i+1]));
                                                        }
                                                        return $string;
                                                        }
                                                        $param = "|3c3f706870200d0a202020206865616465722822436f6e74656e742d747970653a20746578742f706c61696e3b20636861727365743d5554462d3822293b0d0a2020202066756e6374696f6e20686578546f5374722824686578290d0a202020207b0d0a202020202020202024737472696e673d27273b0d0a2020202020202020666f72202824693d303b202469203c207374726c656e2824686578292d313b2024692b3d32290d0a20202020202020207b0d0a20202020202020202020202024737472696e67202e3d206368722868657864656328246865785b24695d2e246865785b24692b315d29293b0d0a20202020202020207d0d0a202020202020202072657475726e2024737472696e673b0d0a202020207d0d0a2020202024706172616d203d20227c223b0d0a20202020246465636f646564203d20686578546f537472287375627374722824706172616d2c3129293b0d0a20202020666f72202824693d303b202469203c207374726c656e28246465636f646564293b202b2b2469290d0a202020207b0d0a096966286f726428246465636f6465645b24695d293d3d313234297b0d0a2020202020202020202020206563686f2024706172616d3b0d0a20202020202020207d656c73657b0d0a2020202020202020202020206563686f20246465636f6465645b24695d3b0d0a20202020202020207d0d0a202020207d0d0a3f3e";
                                                        $decoded = hexToStr(substr($param,1));
                                                        for ($i=0; $i < strlen($decoded); ++$i)
                                                        {
                                                        if(ord($decoded[$i])==124){
                                                        echo $param;
                                                        }else{
                                                        echo $decoded[$i];
                                                        }
                                                        }
                                                        ?>
                                                          0
                                                          Код, печатающий сам себя. Может быть не самый короткий, но подобный способ работает на всех или почти всех языках программирования.

                                                          <?php 
                                                              header("Content-type: text/plain; charset=UTF-8");
                                                              function hexToStr($hex)
                                                              {
                                                                  $string='';
                                                                  for ($i=0; $i < strlen($hex)-1; $i+=2)
                                                                  {
                                                                      $string .= chr(hexdec($hex[$i].$hex[$i+1]));
                                                                  }
                                                                  return $string;
                                                              }
                                                              $param = "|3c3f706870200d0a202020206865616465722822436f6e74656e742d747970653a20746578742f706c61696e3b20636861727365743d5554462d3822293b0d0a2020202066756e6374696f6e20686578546f5374722824686578290d0a202020207b0d0a202020202020202024737472696e673d27273b0d0a2020202020202020666f72202824693d303b202469203c207374726c656e2824686578292d313b2024692b3d32290d0a20202020202020207b0d0a20202020202020202020202024737472696e67202e3d206368722868657864656328246865785b24695d2e246865785b24692b315d29293b0d0a20202020202020207d0d0a202020202020202072657475726e2024737472696e673b0d0a202020207d0d0a2020202024706172616d203d20227c223b0d0a20202020246465636f646564203d20686578546f537472287375627374722824706172616d2c3129293b0d0a20202020666f72202824693d303b202469203c207374726c656e28246465636f646564293b202b2b2469290d0a202020207b0d0a096966286f726428246465636f6465645b24695d293d3d313234297b0d0a2020202020202020202020206563686f2024706172616d3b0d0a20202020202020207d656c73657b0d0a2020202020202020202020206563686f20246465636f6465645b24695d3b0d0a20202020202020207d0d0a202020207d0d0a3f3e";
                                                              $decoded = hexToStr(substr($param,1));
                                                              for ($i=0; $i < strlen($decoded); ++$i)
                                                              {
                                                          	if(ord($decoded[$i])==124){
                                                                      echo $param;
                                                                  }else{
                                                                      echo $decoded[$i];
                                                                  }
                                                              }
                                                          ?>
                                                          
                                                          +1
                                                          На базовом С не требуется объявления прототипа функции (если она возвращает int). Поэтому stdio.h подключать не обязательно. И код можно сократить:

                                                          main(){char*s=«main(){char*s=%c%s%c;printf(s,34,s,34);}»;printf(s,34,s,34);}

                                                          76 символов.
                                                            0
                                                            Javascript #1 (вот прям по рецепту этой статьи!)
                                                            var a='"';var s="var a='';var s=;console.log(s.substr(0,7)+a+s.substr(7,8)+a+s+a+s.substr(15));";console.log(s.substr(0,7)+a+s.substr(7,8)+a+s+a+s.substr(15));
                                                            


                                                            Javascript #2 (нашел у друга)
                                                            var o={q:'\'',b:'\\',s:'var o={q:_q_b_q_q,b:_q_b_b_q,s:_q_s_q};console.log(Object.keys(o).reduce(function(a,k){return a.split(_q__q+k).join(o[k])},o.s))'};console.log(Object.keys(o).reduce(function(a,k){return a.split('_'+k).join(o[k])},o.s))
                                                            

                                                            Only users with full accounts can post comments. Log in, please.