Как нам отдохнуть от писания больших и полезных программ

А.П.Сапожников


Я начну с того, что сразу же попытаюсь предложить ответ на вопрос, вынесенный в заголовок этой публикации: написать маленькую и абсолютно бесполезную программу!

Нынешние поколения программистов скорее всего не помнят древнюю книгу Чарли Уэзерелла "Этюды для программистов". В русском переводе она вышла у нас в 1982 году в издательстве "Мир" под редакцией Ю.М.Баяковского. Здесь я позволю себе процитировать тему самого короткого упражнения, сформулированного в этой книге, и привести несколько решений, предложенных в разные годы аспирантами УНЦ ОИЯИ и студентами Дубненского филиала МИРЭА. Надеюсь, что кто-нибудь из упомянутых здесь авторов встретит этот текст на просторах Интернета и прольет слезу умиления над своими деяниями давно минувших лет. Итак, цитируем то, что сейчас принято считать первоисточником:

Тема. Напишите программу, печатающую копию собственного исходного текста. Вывод не должен содержать "управляющих карт" или другой информации, зависящей от системы. Печатается только то, что перфорируется для компилятора. Однако Ваша программа ничего не должна вводить; ей не следует опираться на системные "штучки", например, на знание того, что конкретный компилятор оставляет копию исходной программы в непомеченном COMMON-блоке. Проследите, чтобы программа давала одинаковый результат независимо от места и времени выполнения.

Указания исполнителю. Не поддавайтесь отчаянию и страху, даже если тринадцатая попытка оказалась неудачной! Подобные программы называются интроспективными, и существует теорема, что интроспективную программу можно написать на любом "достаточно мощном" языке. Все обычные языки программирования - достаточно мощные. Для решения требуется лишь взглянуть на язык под соответствующим углом зрения. Программа, вероятно, займет не более 30-40 строк.

Инструментовка. Годится любой язык.

Длительность исполнения. Одному исполнителю на 1 неделю.

Google дает достаточно много интересных ссылок в ответ на запрос словосочетания "интроспективные программы". В частности, поражает воображение гигантская коллекция Gary P.Thompson II (http://www.nyx.net/~gthompso/quine.htm), содержащая образчики интроспективных программ на 50 языках! Ниже приводится моя собственная скромная коллекция решений этого этюда, накопленная за 10 лет преподавания программирования студентам, соблазняемым возможностью получить зачет - "автомат". Дабы исключить возможность тупого списывания из интернета, (который к началу 90-х годов уже был доступен пытливым юным умам), исполнителям предлагалось оставить в тексте произведения свою фамилию.

Collection of introspective programs:

Topic from Charles Wetherell: program printing itself.

1. FORTRAN (Alexander Sapozhnikov, 1988)


      PROGRAM Fortran_Introspective  ! A.P.Sapozhnikov. 1988
      CHARACTER*54 M1,M2,M3,M4,M5
      DATA M1/54HPROGRAM Fortran_Introspective  ! A.P.Sapozhnikov. 1988/
      DATA M2/54HCHARACTER*54 M1,M2,M3,M4,M5                           /
      DATA M3/54HPRINT M4,M1,M2,1,M1,2,M2,3,M3,4,M4,5,M5,M3,M5         /
      DATA M4/54H(2(7X,A54/)5(7X,6HDATA M,I1,4H/54H,A54,1H//)(7X,A54)) /
      DATA M5/54HEND   ! Ch.Wetherell. Etudes for programmers. 1978.   /
      PRINT M4,M1,M2,1,M1,2,M2,3,M3,4,M4,5,M5,M3,M5
      END   ! Ch.Wetherell. Etudes for programmers. 1978.

2. C++ (Pavel Pogodin, 1993)


#include <stdio.h>
#define u(c) putchar(c);
#define p(s) u(13)u(34)fputs(s, stdout);u(34)u(44)u(10);
main() {
char *c[] = {
"char *c[] = {",
"#include <stdio.h>",
"#define u(c) putchar(c);",
"#define p(s) u(13)u(34)fputs(s, stdout);u(34)u(44)u(10);",
"main() {",
"puts(c[1]);puts(c[2]);puts(c[3]);puts(c[4]);puts(c[0]);p(c[0])p(c[1])",
"p(c[2])p(c[3])p(c[4])p(c[5])p(c[6])u(125)u(59)u(10)puts(c[5]);puts(c[6]);}",
};
puts(c[1]);puts(c[2]);puts(c[3]);puts(c[4]);puts(c[0]);p(c[0])p(c[1])
p(c[2])p(c[3])p(c[4])p(c[5])p(c[6])u(125)u(59)u(10)puts(c[5]);puts(c[6]);}

3. PROLOG (Igor Nosov, 1994)


 goal	S="\n goal	S=char_int(K,34),char_int(P,92),char_int(Z,44),",
	S1="frontstr(1,S,B,X),frontstr(8,X,A,O),frontstr(5,A,_,Y),% HOCOB",
	S2="frontstr(2,Y,T,R),write(A,K,P,n,X,K,Z,B,T,1,R,K,S1,K,Z,B,T),",
	S3="write(2,R,K,S2,K,Z,B,T,3,R,K,S3,K,Z,B,O,B,S1,B,S2,B,S3,B),exit",
char_int(K,34),char_int(P,92),char_int(Z,44),
frontstr(1,S,B,X),frontstr(8,X,A,O),frontstr(5,A,_,Y),% HOCOB
frontstr(2,Y,T,R),write(A,K,P,n,X,K,Z,B,T,1,R,K,S1,K,Z,B,T),
write(2,R,K,S2,K,Z,B,T,3,R,K,S3,K,Z,B,O,B,S1,B,S2,B,S3,B),exit

4. BASIC (Igor Nosov, 1994)


S$="S$=K$=chr$(34):cls:? left$(S$,3);K$;S$;K$,right$(S$,59)' HOCOB"
K$=chr$(34):cls:? left$(S$,3);K$;S$;K$,right$(S$,59)' HOCOB

5. C (Elena Akishina, 1996)


#include <stdio.h>
#include <string.h>
int i = 0; char buf[100][80];
const char pk[] = {'p','(',34,0}, kp[] = {34,')',';',0};
const char ending[] = {'r','(',')',';','}',';',0};
const char fmt[] = {'%','s',10,0};
const char fmp[] = {'%','s',0};/* Akishina E. MEPhI K8 - 08 */
void p(const char* s) { printf(fmt, s); strcpy(buf[i++], s); }
void r(void) { int j; for (j = 0; j < i; j++) {
printf(fmp, pk); printf(fmp, buf[j]); printf(fmt, kp); }
printf(fmt, ending); }
void main() {
p("#include <stdio.h>");
p("#include <string.h>");
p("int i = 0; char buf[100][80];");
p("const char pk[] = {'p','(',34,0}, kp[] = {34,')',';',0};");
p("const char ending[] = {'r','(',')',';','}',';',0};");
p("const char fmt[] = {'%','s',10,0};");
p("const char fmp[] = {'%','s',0};/* Akishina E. MEPhI K8 - 08 */");
p("void p(const char* s) { printf(fmt, s); strcpy(buf[i++], s); }");
p("void r(void) { int j; for (j = 0; j < i; j++) {");
p("printf(fmp, pk); printf(fmp, buf[j]); printf(fmt, kp); }");
p("printf(fmt, ending); }");
p("void main() {");
r();};

6. C (Nikolai Kornejchuk, 1998)


#include <stdio.h> //Kornejchuk N.A.98
#include <conio.h>
char X[217]={
 35,105,110, 99,108,117,100,101, 32, 60,115,116,100,105,111, 46,104, 62, 32, 47,
 47, 75,111,114,110,101,106, 99,104,117,107, 32, 78, 46, 65, 46, 57, 56, 10, 35,
105,110, 99,108,117,100,101, 32, 60, 99,111,110,105,111, 46,104, 62, 10, 99,104,
 97,114, 32, 88, 91, 50, 49, 55, 93, 61,123, 10,125, 59, 10,118,111,105,100, 32,
109, 97,105,110, 40, 41, 10,123, 10, 32, 32,105,110,116, 32, 73, 59, 10, 32, 32,
 99,108,114,115, 99,114, 40, 41, 59, 10, 32, 32,102,111,114, 32, 40, 73, 61, 48,
 59, 73, 60, 53, 50, 59, 73, 43, 43, 41, 32,112,114,105,110,116,102, 40, 34, 37,
 99, 34, 44, 88, 91, 73, 93, 41, 59, 10, 32, 32,102,111,114, 32, 40, 73, 61, 48,
 59, 73, 60, 50, 49, 55, 59, 73, 43, 43, 41, 32,112,114,105,110,116,102, 40, 34,
 37, 51,100, 44, 34, 44, 88, 91, 73, 93, 41, 59, 10, 32, 32,102,111,114, 32, 40,
 73, 61, 53, 49, 59, 73, 60, 50, 49, 55, 59, 73, 43, 43, 41, 32,112,114,105,110,
116,102, 40, 34, 37, 99, 34, 44, 88, 91, 73, 93, 41, 59, 10,125, 10,
};
void main()
{
  int I;
  clrscr();
  for (I=0;I<52;I++) printf("%c",X[I]);
  for (I=0;I<217;I++) printf("%3d,",X[I]);
  for (I=51;I<217;I++) printf("%c",X[I]);
}

7. Pascal (Sergey Badaev, 2002)


Program Pascal_Introspective;   { Бадаев С.  МИРЭА 2002 }
Var i,j:integer;
Const Blan=#32; Apos=#39; Comma=#44; Skob=#41; Semi=#59; Lines=15; L2=5;
  T:array[1..Lines] of string = (
  'Program Pascal_Introspective;   { Бадаев С.  МИРЭА 2002 }',
  'Var i,j:integer;',
  'Const Blan=#32; Apos=#39; Comma=#44; Skob=#41; Semi=#59; Lines=15; L2=5;',
  '  T:array[1..Lines] of string = (',
  'begin',
  '  for i:=1 to Lines do begin',
  '    if i=L2 then begin',
  '      for j:=1 to Lines do begin Write(Blan,Blan,Apos,T[j],Apos);',
  '        if j<>Lines then WriteLn(Comma) else WriteLn;',
  '      end;',
  '      WriteLn(Blan,Blan,Skob,Semi);',
  '    end;',
  '    if i<>Lines then WriteLn(T[i]) else Write(T[i]);',
  '  end;',
  'end.'
  );
begin
  for i:=1 to Lines do begin
    if i=L2 then begin
      for j:=1 to Lines do begin Write(Blan,Blan,Apos,T[j],Apos);
        if j<>Lines then WriteLn(Comma) else WriteLn;
      end;
      WriteLn(Blan,Blan,Skob,Semi);
    end;
    if i<>Lines then WriteLn(T[i]) else Write(T[i]);
  end;
end.

8. PHP (Eugenij Litviak, 2003)


<?php

#  Литвяк Е. МИРЭА, 2003

$y="function q(\$q){
   \$q=str_replace('\\\\','\\\\\\\\',\$q);
   \$q=str_replace('\$','\\\\\$',\$q);
   \$q=str_replace('\\n','\\\\n',\$q);
   \$q=str_replace('\"','\\\\\"',\$q);
   return \$q;

}

echo \"<?php\\n\\n";echo \"#  Литвяк Е. МИРЭА, 2003\\n\\n";
echo '\$y=\"'.q(\$y).'\";'; echo \"\\n\\neval(\";echo '\$y);'; echo \"\\n\\n\\n\";

";

eval($y);

?>

Вот собственно и все, что я хотел сохранить для следующего поколения молодых читателей. Я закончу цитатой из авторитетного Дональда Кнута (D.Knuth), взятой из его лекции при вручении премии Тьюринга.

"Каждый из нас лишь выиграет, создавая время от времени "игрушечные" программы с заданными искусственными ограничениями, заставляющими нас до предела напрягать свои способности. ... Искусство решения мини-задач на пределе своих возможностей оттачивает наше умение для реальных задач."


    А.П.Сапожников,    Дубна, 2010