Куайны (бесполезная программа в программе)
Куайн (от анг. quine) — программа, результатом работы которой является собственный исходный код. Сразу оговоримся: программы, которые обращаются к файлам или производят считывание с клавиатуры куайнами не являются. Более серьезное ограничение: программы, которые могут напрямую получить доступ к своему исходному коду (средствами языка), также не являются куайнами.
А существуют ли они?
Несмотря на простую формулировку задания, потратив немного времени на ее решение, возникает вопрос: существуют ли вообще такие программы? Ответ: Да!
Более того, куайн существует в любом языке, способном выводить произвольную вычисляемую строку! Впервые эта идея была описана Полом Братли и Жаном Милло. А первым куайном считается программа, написанная на языке Atlas Autocode Хэмишем Дюаром.
А на современных языках?
program autobiografija (output); var c : array[1..14] of string[60]; i : integer; begin c[ 1]:='program autobiografija (output); '; c[ 2]:=' var c : array[1..14] of string[60]; '; c[ 3]:=' i : integer; '; c[ 4]:='begin '; c[ 5]:='for i := 1 to 4 do writeln(c[i]); '; c[ 6]:='for i := 1 to 13 do writeln(c[14,1],c[14,2],i:2,c[14,5], '; c[ 7]:=' c[14,6],c[14,7],c[14,8],c[i],c[14,8],c[14,9]); '; c[ 8]:='for i := 1 to 8 do write(c[14,i]); '; c[ 9]:='for i := 1 to 8 do write(c[14,i]); '; c[10]:='for i := 8 to 60 do write(c[14,i]); '; c[11]:='writeln(c[14,8],c[14,9]); '; c[12]:='for i := 5 to 13 do writeln(c[i]); '; c[13]:='end. '; c[14]:='c[14]:=''; '; for i := 1 to 4 do writeln(c[i]); for i := 1 to 13 do writeln(c[14,1],c[14,2],i:2,c[14,5], c[14,6],c[14,7],c[14,8],c[i],c[14,8],c[14,9]); for i := 1 to 8 do write(c[14,i]); for i := 1 to 8 do write(c[14,i]); for i := 8 to 60 do write(c[14,i]); writeln(c[14,8],c[14,9]); for i := 5 to 13 do writeln(c[i]); end.
Итак. Мы убедились, что такие программы существуют, а теперь немного теории о том, как их сделать.
Грабли
Интуитивно понятно, что нужно вывести значение переменной, в которой хранится частичный код программы. Почему частичный? Потому что само присваивание переменной тоже должно оказаться в значении переменной. Иными словами, значение переменной должно копировать само себя, из-за чего возникает бесконечная рекурсия. Неприятный момент.
Чтобы исправить положение, вообще не будем вносить в переменную сам факт присваивания. То есть:
После, во время вывода подставим значение с в её же определение.
Хорошо, но возникает проблема с кавычками. Языки, в которых определены одинарные и двойные кавычки, справляются с проблемой хорошо (мы говорим о том, что можно создать переменную q=» ‘ «, а потом вывести ее значение в ее определение), но что делать, например, с языком С? Экранирование, очевидно, не поможет, так как его тоже надо экранировать… В этом случае, можно задать кавычки кодом символа и вывести его.
Теперь остается проблема вставки строки в выходную строку с. Здесь вспомним про printf и всю его мощь.
Чаще всего методы взятия подстроки умеют брать её до конца:
Практика
Мы не будем лишать вас удовольствия написать собственный куайн, но покажем парочку экзотических примеров.
PHP
SQL
SELECT REPLACE(REPLACE('SELECT REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") AS Quine',CHAR(34),CHAR(39)),CHAR(36),'SELECT REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") AS Quine') AS Quine
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:])
Perl
Высший пилотаж
Куайн, выводящий собственный код, это, конечно, хорошо. Но как вам идея куайна, который пишет другой код, являющийся куайном. Причем результатом работы последнего является первоначальный куайн! Заманчиво, не так ли?
Такого рода программы называются цепные куайны, и на сегодняшний день, самый большой цепной куайн (длина цепи уже достигла 100 языков) написан японцем Юсукэ Эндо.
$ sudo apt-get install afnix algol68g aplus-fsf asymptote \ ats-lang-anairiats bash bc bf boo bsdgames cduce clisp clojure1.4 \ cmake coffeescript dc ecere-sdk emacs23 erlang f2c falconpl \ fp-compiler fsharp g++ gambas3-script gap gauche gawk gcc gdc genius \ gforth gfortran ghc ghostscript gnat gnu-smalltalk gnuplot gobjc \ golang gpt gri groff groovy haxe icont iconx intercal iverilog \ jasmin-sable julia kaya libgd2-xpm-dev libpng12-dev lisaac llvm lua5.2 \ make maxima mlton mono-devel mono-mcs mono-vbnc nasm neko nickle ocaml \ octave open-cobol openjdk-6-jdk pari-gp parrot perl php5-cli pike7.8 \ python r-base ratfor regina-rexx rhino ruby2.0 scala scilab slsh \ spl-core swi-prolog tcl ucblogo valac xsltproc yorick zoem
Ну и, соответственно, результат каждой программы нужно запускать самим:
$ ruby QR.rb > QR.scala $ scalac QR.scala && scala QR > QR.scm $ gosh QR.scm > QR.sci $ scilab -nw -nb -f QR.sci > QR.bash $ bash QR.bash > QR.sl $ slsh QR.sl > QR.st $ gst QR.st > QR.spl $ splrun QR.spl > QR.sml $ mlton @MLton fixed-heap 200M -- QR.sml && ./QR > QR.sq $ ruby vendor/subleq.rb QR.sq > QR.tcl $ tclsh QR.tcl > QR.t $ ruby vendor/thue.rb QR.t > QR.unl $ ruby vendor/unlambda.rb QR.unl > QR.vala $ valac QR.vala && ./QR > QR.v $ iverilog -o QR QR.v && ./QR -vcd-none > QR.vb $ vbnc QR.vb && mono ./QR.exe > QR.ws $ ruby vendor/whitespace.rb QR.ws > QR.xslt $ xsltproc QR.xslt > QR.yorick $ yorick -batch QR.yorick > QR.azm $ zoem -i QR.azm > QR.+ $ a+ QR.+ > qr.adb $ gnatmake qr.adb && ./qr > QR.als $ axi QR.als > QR.a68 $ a68g QR.a68 > QR.ante $ ruby vendor/ante.rb QR.ante > QR.asy $ asy QR.asy > QR.dats $ atscc -o QR QR.dats && ./QR > QR.awk $ awk -f QR.awk > QR.bc $ BC_LINE_LENGTH=4000000 bc -q QR.bc > QR.bef $ cfunge QR.bef > QR.Blc $ ruby vendor/blc.rb < QR.Blc >QR.boo $ booi QR.boo > QR.bf $ bf QR.bf > QR.c $ gcc -o QR QR.c && ./QR > QR.cpp $ g++ -o QR QR.cpp && ./QR > QR.cs $ mcs QR.cs && mono QR.exe > QR.cd $ cduce QR.cd > QR.chef $ PERL5LIB=vendor/local/lib/perl5 compilechef QR.chef QR.chef.pl && perl QR.chef.pl > QR.clj $ clojure QR.clj > QR.cob $ cobc -O2 -x QR.cob && ./QR > QR.coffee $ coffee QR.coffee > QR.lisp $ clisp QR.lisp > QR.d $ gdc -o QR QR.d && ./QR > QR.dc $ dc QR.dc > QR.ec $ ecp -c QR.ec -o QR.sym && ecc -c QR.ec -o QR.c && ecs -console QR.sym QR.imp -o QR.main.ec && ecp -c QR.main.ec -o QR.main.sym && ecc -c QR.main.ec -o QR.main.c && gcc -o QR QR.c QR.main.c -lecereCOM && ./QR > QR.el $ emacs -Q --script QR.el > QR.erl $ escript QR.erl > QR.fsx $ fsharpc QR.fsx -o QR.exe && mono QR.exe > QR.fal $ falcon QR.fal > QR.false $ ruby vendor/false.rb QR.false > QR.fs $ gforth QR.fs > QR.f $ f2c QR.f && gcc -o QR QR.c -L/usr/lib -lf2c -lm && ./QR > QR.f90 $ gfortran -o QR QR.f90 && ./QR > QR.gbs $ gbs3 QR.gbs > QR.g $ gap -q QR.g > QR.gel $ genius QR.gel > QR.plt $ gnuplot QR.plt > QR.go $ go run QR.go > QR.gpt $ gpt -o QR QR.gpt && ./QR > QR.gri $ gri QR.gri > QR.groovy $ groovy QR.groovy > QR.hs $ ghc QR.hs && ./QR > QR.hx $ haxe -main QR -neko QR.n && neko QR.n > QR.icn $ icont -s QR.icn && ./QR > QR.i $ ick -bfO QR.i && ./QR > QR.j $ jasmin QR.j && java QR > QR.java $ javac QR.java && java QR > QR.js $ rhino QR.js > QR.jl $ julia QR.jl > QR.k $ kayac QR.k && ./QR > QR.lazy $ lazyk QR.lazy > qr.li $ lisaac qr.li && ./qr > QR.ll $ llvm-as QR.ll && lli QR.bc > QR.logo $ logo QR.logo > QR.lol $ lci QR.lol > QR.lua $ lua QR.lua > QR.mk $ make -f QR.mk > QR.mac $ maxima -q --init-mac=QR.mac > QR.il $ ilasm QR.il && mono QR.exe > QR.asm $ nasm -felf QR.asm && ld -m elf_i386 -o QR QR.o && ./QR > QR.neko $ nekoc QR.neko && neko QR.n > QR.5c $ nickle QR.5c > QR.m $ gcc -o QR QR.m && ./QR > QR.ml $ ocaml QR.ml > QR.octave $ octave -qf QR.octave > QR.ook $ ruby vendor/ook-to-bf.rb QR.ook QR.ook.bf && bf QR.ook.bf > QR.gp $ gp -f -q QR.gp > QR.pasm $ parrot QR.pasm > QR.pas $ fpc QR.pas && ./QR > QR.pl $ perl QR.pl > QR.php $ php QR.php > QR.png $ npiet QR.png > QR.pike $ pike QR.pike > QR.ps $ gs -dNODISPLAY -q QR.ps > QR.ppt $ ppt -d < QR.ppt >QR.prolog $ swipl -q -t qr -f QR.prolog > QR.py $ python QR.py > QR.R $ R --slave -f QR.R > QR.r $ ratfor -o QR.r.f QR.r && gfortran -o QR QR.r.f && ./QR > QR.rexx $ rexx ./QR.rexx > QR2.rb
Несложно убедиться в том, что результатом всей этой цепи является первоначальная программа.
Ах да, языки программирования в куайне расположены в алфавитном порядке.
Quine на Python
Программисты тоже умеют развлекаться, так что давайте сегодня развлечемся и напишем quine (квайн). Квайн – это такая программа, которая выводит на экран свой же код, ни больше, ни меньше. Сразу договоримся, что пустая программа на Python, которая ничего не выводит, не считается квайном; это не интересно.
В Python у нас есть чудо-переменная, которая хранит путь к текущему интерпретируемому файлу, поэтому можно сделать так:
Эта программа открывает свой же файл, читает и печатает его целиком. Но это жульничество, потому что в квайнах не принято читать файлы. Хорошо, а что если назвать файл print(__file__) , записать в него print(__file__) и выполнить python «print(__file__)» . Будет работать, но можешь вот без этих трюков, чисто кодом? Да без проблем!
Нам нужно что-то печатать, значит берем print:
Программа начинается с print…, значит и печатать будем тоже самое:
Не получается, потому что у нас тут уже два print да кавычки, а печатается только один. Так можно плодить print до бесконечности, но все равно не будет хватать одного в выводе. Будем решать поэтапно. Давайте заведем переменную s с кодом нашей программы.
Но код теперь начинается с s=, исправим:
Смотрите, уже похоже, осталось только на место знака вопроса воткнуть содержимое строки s из оригинального кода. Это самый важный момент. Используем format, а точнее s.format(s), который в определенном месте строки s вставит саму же строку s, таким образом, мы «разрываем рекурсию»:
>>> s='s=<>;print(s)';print(s.format(s)) s=s=<>;print(s);print(s)
Отлично! Но тут два недостатка: во-первых, не забыть добавить s.format(s) в саму строку s:
>>> s='s=<>;print(s.format(s))';print(s.format(s)) s=s=<>;print(s.format(s));print(s.format(s))
Во-вторых, нужно вернуть на место кавычки. Не зря я недавно рассказывал о флагах преобразования строк. Используем флаг в формате, чтобы вывести repr(s) , который для строк содержит одинарные кавычки:
>>> s='s=;print(s.format(s))';print(s.format(s)) s='s=;print(s.format(s))';print(s.format(s))
Ура! Квайн готов и работает!
Вы можете сделать квайн короче, используя другой стиль форматирования строк через процент: заменяется на %r , s.format(s) на s%s , плюс экранируется процент внутри самой строки s%%s (%% понимается как сам знак процента, а не как место для подстановки):
>>> s='s=%r;print(s%%s)';print(s%s) s='s=%r;print(s%%s)';print(s%s)
🐉 Специально для канала @pyway. Подписывайтесь на мой канал в Телеграм @pyway 👈
Программа, выводящая сама себя
Цель — написать такую программу, используя минимальное количество средств (я знаю только питон, поэтому привожу для него: хотелось бы использовать только команду print, кавычки, умножение на число и сложение строк — известно решение, использующее ещё точку с запятой и функцию chr, превращающую номер в символ под данным номером в юникоде).
a = «b = chr(97) + chr(32) + chr(61) + chr(32) + chr(34); b += a; print(b + chr(34)); print(a)»
b = chr(97) + chr(32) + chr(61) + chr(32) + chr(34); b += a; print(b + chr(34)); print(a)
По-видимому, очевидного доказательства невозможности обойтись названными мною выше средствами не существует, а если есть неочевидное, то так будет даже интереснее. Но, каков бы ни был ответ на этот вопрос, он всё равно должен быть интересен.
Не запускается сама программа
Не могу найти ошибку import random print ("\tДобро пожаловать в игру ‘Отгадай число’ !") print.
Программа должна удалить сама себя
Я хочу чтобы программа удалила свой .exe файл, то бишь саму себя, с.
Программа выводящая чередование чисел
Написать программу, распечатывающую введенные числа в следующем виде: вначале печатается.
Программа, выводящая чётные степени чисел
Со стандартного потока считываются НАТУРАЛЬНЫЕ числа a и n. Разработать программу, вычисляющую.
Не запускается интерфейс и сама программа Translator
Сделал переводчик на PyQt5 , переделал его в py файл написал программу , ошибок вроде нету но.