python
Код: Выделить всё
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:])
C
Код: Выделить всё
#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;}
JavaScript (используя хак)
Код: Выделить всё
(function quine() {
console.log("(" + quine.toString() + "())");
}())
Текст
http://habrahabr.ru/post/128191/
- Спойлер
- Как писать квайны
Ненормальное программирование*
Введение
Многие программисты считают написание квайнов (программ, выводящих свой исходный код) непосильной задачей. И действительно — все эти цепные квайны и квайны различного порядка, при взгляде на которые можно потеряться в, казалось бы, бессмысленном наборе символов…
Однако, на самом деле, написать квайн на каком-либо языке не так сложно, как кажется. Сейчас я расскажу, как сделать это на различных языках программирования. Более того, мы не будем использовать «хаки» интерпретеруемых языков вроде операции вывода исходного кода и функций типа 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. Не бойтесь трудностей — достаточно попробовать, и всё получится!
квайн, ненормальное программирование, python, ruby, perl, php, си, c
+76
8527
98
Александр Борзунов @hx0 карма130,0 рейтинг0,0
Похожие публикации
Учебник программирования «Hello Ruby» для детей собрал $288 000 на Kickstarter (32)
Публикуем веб приложения Ruby, Python, Node.js, Perl и Java в Azure Cloud Services (4)
Шаблон программирования «Текучий интерфейс» в PHP. Свежий взгляд (67)
Занимательное функциональное программирование в Ruby (20)
Online — обучение программированию на Ruby без хабраэффекта (11)
Комментарии (55)
–7 terrance11 сентября 2011 в 13:48#
Квайн на Basic:
LIST
+7 AbnormalHead11 сентября 2011 в 20:09#↵↑
Вы, по-видимому, не читали данную в статье ссылку. Там прямо в первом абзаце сказано, что LIST — является грязным хаком.
0 sova12 сентября 2011 в 23:55#↵↑
некоторым людям нравятся грязные хаки
+4 sst23911 сентября 2011 в 13:59#
HQ9+
Q
+8 silencer11 сентября 2011 в 14:11#↵↑
Можно еще на 1 байт сократить размер программы. Тогда будет работать на большем количестве языков, а не только на HQ9+.
+5 AbnormalHead11 сентября 2011 в 21:57#↵↑
Вырождение программы до нулевого размера также не является квайном
0 silencer12 сентября 2011 в 07:41#↵↑
Только после этого случая: 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 vladoos11 сентября 2011 в 14:00#
хм, а к чему это? Я понимаю так — если бы выходные данные можно было подать в качестве входных данных, модифицировать, перекомпилировать, а за тем снова использовать в качестве выходного параметра для самой себя было намного интереснее. Избавляться от рекурсии тут совершенно зря. Рекурсия нужна, встает только вопрос что лучше использовать в качестве выходной стоки для разбора, исходный язык программирования, бинарную строку или нечто промежуточное, типа байт кода.
0 ilyaplot11 сентября 2011 в 14:06#
Все таки, как правильно, квайн или куайн
+1 ShpuntiK11 сентября 2011 в 14:24#↵↑
квайн
0 Xlab11 сентября 2011 в 14:48#↵↑
квайн
0 hx011 сентября 2011 в 15:13#↵↑
Оба варианта допустимы.
> Куайн, квайн (англ. quine) — компьютерная программа, которая выдаёт на выходе точную копию своего исходного текста. Следует заметить, что программы, использующие внешние данные, куайнами не считаются; то есть исключается прочтение текста программы из файла, ввод его с клавиатуры и так далее. Кроме того, не считается куайном «программа», не содержащая вообще никакого кода (вырожденный случай). (с) Wikipedia
+16 mariofag11 сентября 2011 в 15:18#↵↑
Кавайн ^_^
+1 vicnaum11 сентября 2011 в 19:12#↵↑
кўайн
+1 Ostrovski11 сентября 2011 в 14:16#
Попробуем написать квайн. Для этого возьмём инструкцию языка для вывода и передадим ей как параметр код программы. Однако в коде мы снова используем этот же код и так далее — возникает бесконечная рекурсия. Но что можно сделать для того, чтобы не передавать строковую константу? Решение — поместить строку (копию части кода) в переменную. Для удобства назовём такую строку s-строкой, а переменную с этой строкой — s-переменной. Чтобы и в s-переменной не было рекурсии, мы просто исключим из неё фрагмент задания этой самой переменной. То есть, выглядит это примерно так:
C:
const char *s=«const char *s=;»;
Вы решили взорвать мой мозг, чтобы в понедельник я на работу не вышел? ) Интересная ПЕРЕМЕНная с модификатором const, причем не содержащая «фрагмент задания этой самой переменной» «const char *s=;»
+1 hx011 сентября 2011 в 15:22#↵↑
Поправил)
+1 Ostrovski11 сентября 2011 в 15:34#↵↑
Ухты, спасибо =) В принципе мысль и так была понятна, просто спросонья решил побурчать…
–2 sundoser11 сентября 2011 в 14:47#
Наверно самый простой квайн это в бинарнике (если считать что бинарник это самый машинный язык), открыть свой исполняемый на чтение и вывести в хексах или в бине.
0 hx011 сентября 2011 в 15:14#↵↑
> Куайн, квайн (англ. quine) — компьютерная программа (частный случай метапрограммирования), которая выдаёт на выходе точную копию своего исходного текста. Следует заметить, что программы, использующие внешние данные, куайнами не считаются; то есть исключается прочтение текста программы из файла, ввод его с клавиатуры и так далее. Кроме того, не считается куайном «программа», не содержащая вообще никакого кода (вырожденный случай).
+8 argos11 сентября 2011 в 14:54#
Хороший квайн на питоне нашёл. Сохранить в файл fail.py и запустить:
File «fail.py», line 1
File «fail.py», line 1
^
IndentationError: unexpected indent
+2 argos11 сентября 2011 в 14:59#↵↑
ай, тег code не работает, форматирование съехало.
Вообщем ссылка на оригинал: www.reddit.com/r/Python/comments/k9857/xx010_prin ... formatchr34x/c2ijqff?context=1
0 eugzol11 сентября 2011 в 23:51#↵↑
очень клёво
0 koorchik11 сентября 2011 в 15:06#
Квайн на Perl:
seek( DATA, 0, 0 );
print ;
__DATA__
–1 koorchik11 сентября 2011 в 15:07#↵↑
Вернее
seek( DATA, 0, 0 );
print <DATA>;
__DATA__;
+2 silencer11 сентября 2011 в 18:44#↵↑
Еще проще: open 0; print <0>;
0 valzevul11 сентября 2011 в 15:31#
Более того, мы не будем использовать «хаки» интерпретеруемых языков вроде операции вывода исходного кода и функций типа eval
Простите, а можно поподробнее про вывод кода программы с помощью операции вывода кода программы или функции eval()? На питоне, например.
Даже не подозревал о таком.
0 hx011 сентября 2011 в 16:18#↵↑
Конечно, сразу код так не вывести, но часто это может значительно упростить задачу: только лишь присвоили переменной код, вывели код и исполнили код.
0 valzevul11 сентября 2011 в 16:19#↵↑
Понятно. спасибо.
+3 azproduction11 сентября 2011 в 16:03#
JavaScript (Mozilla JaggerMonkey) с выводом в консоль:
(function quine() {
console.log("(" + quine.toString() + "())");
}())
Т.к. движков JavaScript много, то универсальный квайн написать нельзя
+1 naryl12 сентября 2011 в 20:45#↵↑
Думаю это тоже читерство, но вот более переносимая версия, правда для другого языка. :] Если сделать (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 0xy 2 августа 2013 в 10:56#↵↑
Можно короче
(function () {
console.log("(" + arguments.calee.toString() + "())");
}())
+1 naryl11 сентября 2011 в 16:25#
Без строк.
(=> — это вывод системы)
(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 Amper11 сентября 2011 в 19:08#
Python-читерство =))
print(open(__file__).read())
+2 xonix11 сентября 2011 в 22:21#↵↑
#!/bin/cat
0 powder9611 сентября 2011 в 22:35#↵↑
Штуки как <?php echo file_get_contents(__FILE__); ?> квайнами не сичтаются: «исключается прочтение текста программы из файла» — из википедии.
0 hx011 сентября 2011 в 22:40#↵↑
Мне просто надоело уже википедию копировать, ведь не первый раз уже.
0 Duti_Fruti11 сентября 2011 в 22:49#
Какое практическое применение квайнов? Тема интересная, но пока не могу понять где в практике можно использовать.
+2 naryl11 сентября 2011 в 23:00#↵↑
нигде
+1 naryl12 сентября 2011 в 19:44#↵↑
По оценкам за мои два комментария в этом топике можно подумать что капитанство оценивается на Хабре выше чем написание квайнов.
–1 hx012 сентября 2011 в 19:54#↵↑
Хабр такой хабр.
+2 mekegi11 сентября 2011 в 23:06#↵↑
Также как и решение задач, головоломок ребусов и тд. в реальной жизни мало где находят применения, однако ж действия сии заставляют мозги работать и держат их в тонусе. Так что польза от этого все же есть.
–2 mardy_bum12 сентября 2011 в 02:49#↵↑
На собеседовании могут спросить
0 runcore12 сентября 2011 в 05:43#
<?php printf($a='<?php printf($a=%c%s%c,39,$a,39);',39,$a,39);
0 Mrrl22 сентября 2011 в 12:38#↵↑
А порядок вычисления параметров фукции гарантируется? Точно ли присваивание выполнится до использования $a в качестве третьего параметра? И не надо ли закрыть скобку?
0 runcore22 сентября 2011 в 12:48#↵↑
гарантируется. точно. какую?
0 Mrrl22 сентября 2011 в 13:02#↵↑
Парную к <?php? Или она и не должна закрываться?
0 3vi1_0n312 сентября 2011 в 09:06#
На windows cmd считается за квайн?
<---cut--->
@type %0
<---cut--->
Или это тоже грязный хак?
0 AbnormalHead12 сентября 2011 в 22:39#↵↑
Скорей всего хак. Т.к. явно читает код собственной программы.
0 afiskon12 сентября 2011 в 09:24#
main = putStr (s ++ show s) where s = «main = putStr (s ++ show s) where s = „
+1 ta6aku12 сентября 2011 в 11:39#
чисто для отчетности
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 talmuth12 сентября 2011 в 20:58#
А квайн не лиспе:
T
?
0 naryl12 сентября 2011 в 22:30#↵↑
А я ещё стопицот таких «квайнов» на лиспе знаю:
«a»
«aa»
«aaa»
«100500»
и т.д.
Впрочем, они и в хаскеле, и в руби, и в питоне работают.
Квайн должен явно распечатывать свой исходный код. Если выполнить ваш квайн, сохранив в файл, то ничего не выведется.
0 MadRogue19 сентября 2011 в 14:01#
<?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 MadRogue19 сентября 2011 в 14:05#↵↑
Код, печатающий сам себя. Может быть не самый короткий, но подобный способ работает на всех или почти всех языках программирования.
<?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 Mrrl22 сентября 2011 в 12:34#
На базовом С не требуется объявления прототипа функции (если она возвращает int). Поэтому stdio.h подключать не обязательно. И код можно сократить:
main(){char*s=«main(){char*s=%c%s%c;printf(s,34,s,34);}»;printf(s,34,s,34);}
76 символов.