Зани­ма­тель­ное квай­но­вод­ство. Часть I.


Анно­та­ция

В этом сооб­ще­нии (в пер­вой части серии сооб­ще­ний), после крат­кого вве­де­ния в неко­то­рые основы тео­рии вычис­ли­мо­сти, в основ­ном опи­сы­ва­ются резуль­таты про­стого экс­пе­ри­мента по авто­ма­ти­зи­ро­ван­ному кон­стру­и­ро­ва­нию квайна с исполь­зо­ва­нием вто­рой рекур­сив­ной тео­ремы Клини.

В каче­стве целе­вого языка выбран язык поточ­ного редак­тора [GNU] sed. Изло­же­ние, однако, не спе­ци­фично для этого языка и может трак­то­ваться в более широ­ком кон­тек­сте. Фак­ти­че­ски, в пред­ла­га­е­мом цикле сооб­ще­ний изла­га­ются неко­то­рые под­ходы к систе­ма­ти­че­скому кон­стру­и­ро­ва­нию квай­нов.


Содер­жа­ние:

Вве­де­ние в квай­но­ло­гию

Всем известно «стан­дарт­ное» упраж­не­ние в про­грам­ми­ро­ва­нии, заклю­ча­ю­ще­еся в напи­са­нии про­граммы, печа­та­ю­щей свой соб­ствен­ный текст. За такими само­реп­ли­ци­ру­ю­щи­мися про­грам­мами доста­точно давно закре­пи­лось назва­ние «квайн» (так­же встре­ча­ются вари­анты написания/произношения «ку­айн», «квин», «ку­ин») вве­ден­ное в извест­ной книге Хофштад­тера [26] в честь фило­софа Куайна (Уил­лард Ван Орман Куайн = Willard Van Orman Quine) и его «па­ра­докса Куай­на» [15].

Сами квайны при этом появи­лись раньше. Может быть их исто­рию сле­до­вало бы вести с работ фон Ней­мана по само­реп­ли­ци­ру­ю­щимся [кле­точ­ным] авто­ма­там [9, 48] или даже с работ Чёрча и Хас­ке­ля…

Квайны явля­ются немного пара­док­саль­ными про­грам­мами, в соот­вет­ствии с быто­вой инту­и­цией не могу­щими суще­ство­вать, – ведь может пока­заться, что про­грамма не может выве­сти текст, име­ю­щий длину, рав­ную длине самой про­граммы (должно остаться место для кода, выпол­ня­ю­щего печать). И тем не менее они суще­ствуют (и не тре­буют сжа­тия дан­ных, кото­рое, в соот­вет­ствии с фун­да­мен­таль­ной тео­ре­мой ком­прес­сии, не все­гда воз­можно).

Более того, тео­рия вычис­ли­мо­сти гово­рит, что квайны можно напи­сать на любом [доста­точно выра­зи­тель­ном] языке про­грам­ми­ро­ва­ния. На деле, конечно, слож­ность напи­са­ния и раз­мер резуль­ти­ру­ю­щего кода сильно варьи­ру­ются от языка к языку.

В каче­стве при­ме­ров квай­нов можно при­ве­сти такие поделки (авторы мне не известны):

Квайн на lisp:

((lambda (x)
  (list x (list (quote quote) x)))
(quote
  (lambda (x)
    (list x (list (quote quote) x)))))

Квайн на сте­ко­вом rpn-языке форт (forth):

s" 2dup 115 emit 34 emit 32 emit type 34 emit type cr bye"
2dup 115 emit 34 emit 32 emit type 34 emit type cr bye

(Это одна длин­ная строка, могу­щая отоб­ра­жаться на несколь­ких экран­ных стро­ках.)

Квайн на экс­пе­ри­мен­таль­ном функ­ци­о­наль­ном сте­ко­вом rpn-языке joy [32]:

"dup.putchars.10 putch."
dup.putchars.10 putch.

Квайн для -исчисления(почти то же, что и выше на lisp):


В стан­дарт­ной биб­лио­теке языка C есть функ­ция фор­ма­ти­ро­ван­ного вывода printf, кото­рая может быть исполь­зо­вана [в каче­стве сур­ро­гата -функции] для внед­ре­ния тек­ста про­граммы в него само­го; что-то вроде printf(f, f), только с уче­том тех­ни­че­ских слож­но­стей с вло­жен­ными кавыч­ками для обрам­ле­ния строк. По этому прин­ципу построен такой извест­ный одно­строч­ный мини­квайн на C (здесь не ука­зан заго­ло­воч­ный файл stdio.h, в кото­ром опре­де­лена printf, но в таком виде этот код всё-равно ком­пи­ли­ру­ется и рабо­тает):

char*f="char*f=%c%s%c;main(){printf(f,34,f,34,10);}%c";main(){printf(f,34,f,34,10);}

Мно­гие квайны (в т.ч. боль­шин­ство из при­ве­ден­ных выше) зло­упо­треб­ляют экра­ни­ро­ва­нием строк, спе­ци­аль­ными кодами сим­во­лов (зави­ся­щими от исполь­зу­е­мой в системе коди­ровки, e.g. ASCII) и биб­лио­теч­ными функ­ци­ями под­ста­новки (замены под­строк), «скры­ва­ю­щи­ми» суще­ствен­ную часть про­ис­хо­дя­щих про­цес­сов. «Идео­ло­ги­че­ски» более чистым под­хо­дом пред­став­ля­ется огра­ни­че­ние стро­ко­вых мани­пу­ля­ций кон­ка­те­на­цией строк при одно­вре­мен­ной мини­ми­за­ции экра­ни­ро­ва­ний. Непло­хим образ­чи­ком подоб­ной стра­те­гии напи­са­ния квай­нов явля­ется квайн (на pascal’е) от Dan Hoey (взя­тый мною из [50]):

program s;const bbb='program s;const bbb';a='a';b='b';bb=');writeln(';
aa='''';ab='=''';ba=''';';
aaa='begin writeln(bbb,ab,bbb,ba,a,ab,a,ba,b,ab,b,ba,b,b,ab,bb,ba';
aba='a,a,ab,aa,aa,ba,a,b,ab,ab,aa,ba,b,a,ab,aa,ba,ba';
abb='a,a,a,ab,aaa,ba);writeln(a,b,a,ab,aba,ba);writeln(a,b,b,ab,abb,ba';
baa='b,a,a,ab,baa,ba);writeln(b,a,b,ab,bab,ba);writeln(aaa,bb';
bab='aba,bb);writeln(abb);writeln(bb,baa);writeln(bb,bab)end.';
begin writeln(bbb,ab,bbb,ba,a,ab,a,ba,b,ab,b,ba,b,b,ab,bb,ba);writeln(
a,a,ab,aa,aa,ba,a,b,ab,ab,aa,ba,b,a,ab,aa,ba,ba);writeln(
a,a,a,ab,aaa,ba);writeln(a,b,a,ab,aba,ba);writeln(a,b,b,ab,abb,ba
);writeln(b,a,a,ab,baa,ba);writeln(b,a,b,ab,bab,ba);writeln(aaa,bb
);writeln(aba,bb);writeln(abb);writeln(bb,baa);writeln(bb,bab)end.

(N.B., на самом деле этот квайн запи­сы­ва­ется в одну строку.)

Непло­хое вве­де­ние в напи­са­ние квай­нов можно найти, напри­мер, в [1, 30]; а неплохую кол­лек­цию – в [30, 18].

Искус­ство versus ремесло

Основ­ной при­чи­ной про­ве­де­ния неболь­шого экс­пе­ри­мента, став­шего осно­вой насто­я­щего сооб­ще­ния, яви­лось жела­ние напи­сать, или, точ­нее говоря, попро­бо­вать напи­сать квайн на вход­ном языке поточ­ного редак­тора sed. При­ме­не­ние поис­ко­вых систем для поиска уже име­ю­щихся реше­ний при­вело к обна­ру­же­нию квайна quine.sed [17] из «му­зея квай­нов» [18] (автор: Tsuyusato Kitsune):

s/^/s11^113311;h;s221[1]221122g;s112[2]11222211g;G;s11^22([^3]*22)3322([^22n]*22).22(.*22)$1122122322211/;h;s\1[1]\/\g;s/2[2]/\\/g;G;s/^\([^3]*\)33\([^\n]*\).\(.*\)$/\1\3\2/

(N.B., это одна строка тек­ста.)

Вряд ли у меня вышло бы улуч­шить (напри­мер, сокра­тить) этот квайн. Более того, само­сто­я­тель­ная попытка напи­сать нечто подоб­ное тоже не увен­ча­лась успе­хом. Поэтому было при­нято реше­ние дей­ство­вать более систе­ма­тич­ным обра­зом и вос­поль­зо­ваться уже име­ю­щимся в рас­по­ря­же­нии мате­ма­тики арсе­на­лом гото­вых тео­ре­ти­че­ских средств.

Вто­рая рекур­сив­ная тео­рема Клини имеет своим след­ствием утвер­жде­ние о суще­ство­ва­нии квайна для любого языка про­грам­ми­ро­ва­ния, а кон­струк­тив­ный харак­тер её дока­за­тель­ства дает гото­вый рецепт для напи­са­ния такого «са­мо­ре­пли­ка­то­ра» (ниже об этом будет напи­сано подроб­нее).

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

Попа­да­лись инте­рес­ные исклю­че­ния, e.g. [35, 36], но для слиш­ком абстракт­ных машин. Поэтому я решил повто­рить клас­си­че­ский экс­пе­ри­мент с кон­стру­и­ро­ва­нием квайна на основе дока­за­тель­ства рекур­сив­ной тео­ремы Клини, но выпол­нить этот экс­пе­ри­мент на языке sed (кстати, всё-равно ока­зав­шемся доста­точно удоб­ным для этой задачи, в основ­ном из-за воз­мож­но­сти объ­еди­не­ния скрип­тов их кон­ка­те­на­цией).

Обнов­ле­ние (август, 2020): Уже после напи­са­ния чер­но­вика этого сооб­ще­ния, при­меры при­ме­не­ния вто­рой рекур­сив­ной тео­ремы Клини к прак­ти­че­ским язы­кам про­грам­ми­ро­ва­ния всё-таки отыс­ка­лись на про­сто­рах интер­нета. В [53] можно найти гене­ра­тор квай­нов на JavaScript (ECMAScript), а в [54] – похо­жий гене­ра­тор, но для C++. При­чем в послед­нем при­мере, ука­зан­ная ути­лита пози­ци­о­ни­ру­ется как доста­точно общий инстру­мент для добав­ле­ния рефлек­сив­ных воз­мож­но­стей к языку, с огра­ни­чен­ной штат­ной рефлек­сией (i.e., сге­не­ри­ро­ван­ная про­грамма может как про­сто печа­тать свой соб­ствен­ный текст, так и вычис­лять про­из­воль­ные функ­ции от него, ска­жем, нахо­дить свою длину).

Воз­можно чита­телю так­же будет инте­ресна ана­ло­гич­ная ути­лита [55], пре­об­ра­зу­ю­щая про­из­воль­ную про­грамму на C в квайн.

Резуль­таты экс­пе­ри­мента гово­рят о воз­мож­но­сти авто­ма­ти­че­ской, или «не­твор­че­ской» гене­ра­ции квай­нов, в том числе и на sed.

Эффек­тив­ность и выбор назва­ния серии сооб­ще­ний.

Быть может неко­то­рым пока­за­лось бы несколько более бла­го­звуч­ным слово вроде «квай­но­де­лия», но «квай­но­вод­ство» под­хо­дит куда луч­ше… От части потому, что, как и обе­щают стан­дарт­ные ввод­ные курсы по тео­рии вычис­ли­мо­сти, квайны, кон­стру­и­ру­е­мые пря­мым при­ме­не­нием [вто­рой рекур­сив­ной] тео­ремы Клини, обычно полу­ча­ются, мягко говоря, «не­эф­фек­тив­ны­ми», как по вре­мени, так и по про­стран­ству. Ещё одной мет­ри­кой эффек­тив­но­сти может быть отно­ше­ния объ­ё­мов дан­ных и кода; спе­ци­ально я не иссле­до­вал этот вопрос, так что не могу ска­зать как с этим обстоят дела у авто­ма­ти­че­ски сге­не­ри­ро­ван­ных квай­нов, но невер­няка неопти­мально. :)

Поста­новка задачи

Пусть квайн хра­нится в испол­ня­е­мом файле quine, полу­чен­ным ком­пи­ля­цией его исход­ного тек­ста из файла quine.source. Для меня доста­точно чисто фор­маль­ного выпол­не­ния сле­ду­ю­щего теста:

./quine any_argument > quine.output
diff quine.source quine.output

или ана­ло­гич­ного теста для интер­пре­ти­ру­е­мого языка (здесь interpreter – испол­ня­е­мый файл интер­пре­та­тора или ана­ло­гич­ная внут­рен­няя команда обо­лочки):

interpreter quine.source any_argument > quine.output
diff quine.source quine.output

Для sed это соот­вет­ствует сле­ду­ю­щему тесту:

echo any_argument | sed -f quine.sed > quine.output
diff quine.sed quine.output

Про­верку на три­ви­аль­ность (в част­но­сти, на пустоту) можно выпол­нять и «вруч­ную» (из-за пло­хой фор­ма­ли­зу­е­мо­сти и неко­то­рой субъ­ек­тив­но­сти).

То есть, квайн конеч­ного раз­мера (ска­жем, поме­ща­ю­щийся на жест­кий диск) дол­жен напе­чать свой соб­ствен­ный текст за конеч­ное (и разум­ное время). Абсо­лютно не важно, будут ли исход­ный текст и ском­пи­ли­ро­ван­ная про­грамма весить несколько кило­байт или несколько мега­байт. Гораздо важ­нее, чтобы квайн был истин­ным, т.е. был непу­стым, «со­дер­жа­тель­ным», и не исполь­зо­вал «не­чест­ные» рефлек­сив­ные воз­мож­но­сти (вроде чте­ния сво­его исход­ного тек­ста из файла).

Ввод­ные заме­ча­ния

В тео­рии вычис­ли­мо­сти особо важ­ное место зани­мают несколько инте­рес­ных тео­рем о вычис­ли­мых функ­циях. В этом сооб­ще­нии я хотел бы кратко рас­ска­зать об этих тео­ремах и о неко­то­рых их при­ме­не­ниях.

Для неко­то­рой про­граммы с тек­стом соот­вет­ству­ю­щую ей (частич­ную) вычис­ли­мую функ­цию обычно обо­зна­чают как , однако, воиз­бе­жа­ние нагро­мож­де­ния ниж­них индек­сов далее будет исполь­зо­ваться несколько более удоб­ное обо­зна­че­ние, широко при­ме­ня­ю­ще­еся при изу­че­нии семан­тики язы­ков про­грам­ми­ро­ва­ния, а именно .

Обычно, в тео­рии алго­рит­мов (тео­рии вычис­ли­мо­сти, тео­рии рекур­сии) гово­рят о [гёде­лев­ском] номере про­граммы, а не о её тек­сте, но далее будет исполь­зо­ваться более нагляд­ное и при­выч­ное, по край­ней мере для про­грам­ми­стов, слово текст, ино­гда даже исход­ный код, без пря­мого отсыла к поня­тию нуме­ра­ции из тео­рии вычис­ли­мо­сти. Более того, враз­рез с тра­ди­ци­он­ным сло­во­упо­треб­ле­нием, в насто­я­щем сооб­ще­нии нередко про­грамма и её текст будут отож­деств­ляться, – здесь это про­сто одно и то же. (Крат­ко­сти и про­стоты ради, это сооб­ще­ние будет вообще несколько нефор­маль­ным и не стро­гим.)

При одно­вре­мен­ном рас­смот­ре­нии несколь­ких язы­ков про­грам­ми­ро­ва­ния, как, напри­мер, при работе с транс­ля­то­рами, можно ука­зы­вать язык про­грам­ми­ро­ва­ния в ниж­нем индексе семан­ти­че­ских ско­бок, e.g., . (Кстати, в лите­ра­туре встре­ча­ется и более необыч­ная нота­ция, напри­мер, вообще не исполь­зу­ю­шая какие-либо индексы и скобки, для эко­но­мии места.)

Сим­во­лом будет обо­зна­чено неза­вер­ша­ю­ще­еся вычис­ле­ние, т.е. – это зна­че­ние бес­ко­неч­ного цикла. Равен­ство есть сокра­ще­ние для . (Под­ра­зу­ме­ва­ется, что для неко­то­рых , может выпол­нятся и .)

Тео­рема об уни­вер­саль­ной функ­ции

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

То есть, .

Набро­сок дока­за­тель­ства.

Дока­за­тель­ство может про­во­дится про­сто предъ­яв­ле­нием гото­вого интер­пре­та­тора в виде машины Тью­ринга, т.е. путем постро­е­ние т.н. уни­вер­саль­ной машины Тью­ринга, симу­ли­ру­ю­щей работу любой дру­гой машины Тью­ринга. Понятно, что можно исполь­зо­вать и любую дру­гую модель вычис­ле­ний, e.g., -исчисление. Но здесь, для крат­ко­сти, аргу­мен­та­ция будет менее кон­струк­тив­ной и более абстракт­ной.

Есте­ственно счи­тать про­грам­мами куски тек­ста (i.e., строки) на неко­то­ром языке про­грам­ми­ро­ва­ния. Пусть на стро­ках опре­делён обыч­ный поря­док , а именно, для строк и выпол­ня­ется если (i.e., срав­ни­ва­ются длины) или, в слу­чае , если лек­си­ко­гра­фи­че­ски пред­ше­ствует . Т.о., все про­граммы можно отсор­ти­ро­вать отно­си­тельно , полу­чив в резуль­тате упо­ря­до­чен­ную после­до­ва­тель­ность . Будем счи­тать, что для любой про­граммы можно постро­ить спе­ци­а­ли­зи­ро­ван­ное вычис­ли­тель­ное устрой­ство , пре­об­ра­зу­ю­щее вход­ные дан­ные в выход­ные (эта нота­ция, по-факту, озна­чает запуск устрой­ства с аргу­мен­том ).

Теперь можно опре­де­лить функ­цию . Пол­нота по Тью­рингу поз­во­ляет утвер­ждать, что для любой вычис­ли­мой функ­ции суще­ствует вычис­ли­тель­ное устрой­ство , такое, что ; а раз в после­до­ва­тель­но­сти есть вычис­ли­тель­ные устрой­ства для всех про­грамм, то най­дётся число , такое, что . Полу­ча­ется, что , т.е., функ­ция может исполь­зо­ваться вме­сто любой вычис­ли­мой функ­ции если изве­стен её номер ; дру­гими сло­вами, – уни­вер­саль­ная функ­ция.

Оста­лось пока­зать, что вычис­лима. Для этого надо напи­сать интер­пре­та­тор и скон­стру­ир­вать соот­вет­ству­ю­щее вычис­ли­тель­ное устрой­ство , по пер­вому аргу­менту команд­ной строки извле­ка­ю­щее устрой­ство с индек­сом из после­до­ва­тель­но­сти и запус­ка­ю­щее его, пере­да­вая ему при запуске осталь­ные свои аргу­менты. Т.е., должно выпол­няться . Видно, что – устрой­ство, вычис­ля­ю­щее , а – про­грамма, это дела­ю­щая. Таким обра­зом, – вычис­ли­мая уни­вер­саль­ная функ­ция.

N.B., сама тео­рема не зави­сит от языка про­грам­ми­ро­ва­ния и тре­бует лишь его пол­ноты по Тью­рингу, т.е. воз­мож­но­сти напи­сать для любой вычис­ли­мой функ­ции про­грамму, её вычис­ля­ю­щую; да, зву­чит немного тав­то­ло­гично, и, тем не менее, есть языки не явля­ю­щи­еся Тьюринг-полными: на них нельзя реа­ли­зо­вать неко­то­рые про­граммы, легко пишу­щи­еся на каком-нибудь уни­вер­саль­ном, Тьюринг-полном языке. Суще­ствуют ли про­граммы, не могу­щие быть пере­не­сены на машину Тью­ринга или любую экви­ва­лент­ную ей машину, но могу­щие выпол­няться на каком-нибудь дру­гом ком­пью­тере – инте­рес­ный фило­соф­ский вопрос; тезис Чёрча-Тью­ринга гово­рит, что нет, таких про­грамм не суще­ствует (и дей­стви­тельно, все извест­ные тео­ре­ти­че­ские кон­струк­ции более мощ­ных ком­пью­те­ров кон­флик­туют с теми или иными физи­че­скими зако­нами и в это все­лен­ной рабо­тать не могут).

А вот суще­ство­ва­ние вычис­ли­мой уни­вер­саль­ной функ­ции, по-видимому, не влечёт за собой пол­ноты по Тью­рингу – если взять неко­то­рый под­хо­дя­щий неуни­вер­саль­ный язык, то можно дей­ство­вать как в выше­при­ве­ден­ном наброске дока­за­тель­ства, т.е. можно сге­не­ри­ро­вать и отсор­ти­ро­вать все про­граммы [для этого языка] и вычис­ли­тель­ные устрой­ства, а потом опре­де­лить функ­цию, выби­ра­ю­щую и исполь­зу­ю­щую нуж­ное устрой­ство по его номеру. Если выбран­ный язык будет доста­точно богат, чтобы на нём можно было выра­зить такой выбор и запуск, то мы полу­чим вычис­ли­мую уни­вер­саль­ную функ­цию, правда, уни­вер­саль­ную не для всех вычис­ли­мых функ­ций, а только для функ­ций, реа­ли­зу­е­мых на таком языке.

Фак­ти­че­ски, тео­рема об уни­вер­саль­ной функ­ции гово­рит, что частич­ная (т.е. не явля­ю­ща­яся всюду опре­делён­ной) функ­ция от аргу­мен­тов сама явля­ется вычис­ли­мой (или т.н. частично рекур­сив­ной) функ­цией от аргу­мен­тов (доба­вился ещё один аргу­мент).

Исполь­зо­ва­ние тео­ремы об уни­вер­саль­ной функ­ции в дан­ном сооб­ще­нии будет весьма огра­ни­чен­ным; здесь без неё можно было бы и обой­тись (если не счи­тать тео­рему Род­жерса, см. ниже). И всё-же она в опре­делён­ные моменты будет важна. Кроме этого, стоит обра­тить вни­ма­ние на исполь­зо­ван­ное в дока­за­тель­стве раз­де­ле­ние на про­граммы (тек­сты про­грамм), вычис­ли­тель­ные устрой­ства и их номера, – нам при­шлось исполь­зо­вать устрой­ства, чтобы под­черк­нуть меха­ни­стич­ность и физи­че­скую реа­ли­зу­е­мость вычис­ле­ний, тек­сто­вое пред­став­ле­ние про­грамм, чтобы иметь воз­мож­ность сор­ти­ро­вать и нуме­ро­вать про­граммы/устрой­ства, и, нако­нец, при­шлось выбрать кон­крет­ный (но не един­ствен­ный) спо­соб такой нуме­ра­ции с помо­щью сор­ти­ровки по воз­рас­та­нию отно­си­тельно порядка .

В даль­ней­шем такое раз­де­ле­ние пред­став­ле­ний будет лиш­ним и поэтому, как я уже гово­рил ранее, тек­сты про­грамм, про­граммы и их номера будут отож­деств­ляться. Сам кон­крет­ный спо­соб нуме­ра­ции можно будет счи­тать зада­нием языка про­граммирования, хотя на прак­тике же удобно отож­деств­лять язык про­граммирования с семан­ти­че­ской функ­цией , сопо­став­ля­ю­щей про­грамме функ­цию , где – мно­же­ство, над кото­рым опре­де­лены вычис­ли­мые функ­ции (в боль­шин­стве слу­чаев можно счи­тать ) или с парой . (В сле­ду­ю­щей части этой серии сооб­ще­ний, я поста­ра­юсь уде­лить долж­ное вни­ма­ние поня­тию нуме­ра­ции, но здесь оно свою полез­ность, похоже, пока исчер­пало.)

N.B., Так­же под­ра­зу­ме­ва­ется, что паре зна­че­ний можно сопо­ста­вить вза­имно одно­знач­ным обра­зом (при­чем отоб­ра­же­ние вычис­лимо). Это поз­во­ляет опре­де­лять функ­ции с боль­шим коли­че­ством аргу­мен­тов через функ­ции с мень­шим коли­че­ством, напри­мер . Т.о., тео­ре­ти­че­ски можно вообще обхо­диться только одно­мест­ными функ­ци­ями, но из сооб­ра­же­ний удоб­ства, к такому мини­ма­лизму при­бе­гать, наде­юсь, не при­дётся.

S-m-n тео­рема Клини

Дру­гой, не менее важ­ной тео­ре­мой явля­ется т.н. -теорема (извест­ная так­же как тео­рема о пара­мет­ри­за­ции или ите­ра­тив­ная тео­рема) [14, 3], утвер­жда­ю­щая, что может быть напи­сана про­грамма, умень­ша­ю­щая коли­че­ство аргу­мен­тов путем фик­са­ции зна­че­ний неко­то­рых из них: для про­граммы , реа­ли­зу­ю­щей функ­цию с аргу­мен­тами и для задан­ных зна­че­ний для пер­вых аргу­мен­тов суще­ствует про­грамма для спе­ци­а­ли­зи­ро­ван­ной -местной функ­ции, удо­вле­тво­ря­ю­щая для любых .

Так­же суще­ствует вычис­ли­мая (при­ми­тивно рекур­сив­ная при опре­делён­ном выборе языка про­грам­ми­ро­ва­ния) всюду опре­делён­ная функ­ция, — соб­ственно, -функция, — пре­об­ра­зу­ю­щая про­грамму в про­грамму .

То есть, для про­граммы выпол­ня­ется .

Далее при­го­дится част­ный слу­чай с фик­са­цией всего одного из двух аргу­мен­тов.

Нетрудно видеть, что тео­рема, по-существу, реа­ли­зует частич­ные вычис­ле­ния (спе­ци­а­ли­за­цию про­грамм) [4, 5], т.е. дает воз­мож­ность выпол­нить часть вычис­ле­ний, зави­ся­щих от фик­си­ру­е­мого аргумента(-ов), зара­нее, до выпол­не­ния про­граммы (что может быть исполь­зо­вано, напри­мер, для опти­ми­за­ции кода). Есть неко­то­рое сход­ство между частич­ной спе­ци­а­ли­за­цией и кар­рин­гом из функ­ци­о­наль­ного про­грам­ми­ро­ва­ния.

Дока­за­тель­ство тео­ремы кон­струк­тивно и может быть осно­вано на явном выпи­сы­ва­нии про­граммы (или на явном постро­е­нии машины Тью­ринга), при­сва­и­ва­ю­щей опре­де­лен­ные зна­че­ния исклю­ча­е­мым аргу­мен­там и обес­пе­чи­ва­ю­щей запуск про­граммы при поступ­ле­нии недо­ста­ю­щих дан­ных (такого три­ви­аль­ного алго­ритма недо­ста­точно для напи­са­ния опти­ми­зи­ру­ю­щего спе­ци­а­ли­за­то­ра; но он может быть улуч­шен).

(Само дока­за­тель­ство здесь при­во­дится не будет, а инте­ре­су­ю­щимся реко­мен­ду­ется обра­титься к пер­во­ис­точ­ни­кам или более совре­мен­ным мате­ри­а­лам по тео­рии вычис­ли­мо­сти.)

Стоит отме­тить, что выше­при­ве­ден­ные тео­ремы во мно­гом схожи, и даже в неко­то­ром смысле «взаимо-обратны» – тео­рема об уни­вер­саль­ной функ­ции пре­об­ра­зует «ста­ти­че­ский» пара­метр — про­грамму — в «ди­на­ми­че­ский» аргу­мент, кото­рый может затем при­ни­мать любое зна­че­ние; -теорема, напро­тив, пре­об­ра­зует дина­ми­че­ский аргу­мент в ста­ти­че­ский, при­пи­сы­вая ему апри­ори задан­ное зна­че­ние.

Эта сим­мет­рия хорошо видна если выпи­сать клю­че­вые урав­не­ния обоих тео­рем одно под дру­гим (здесь, для крат­ко­сти, исполь­зу­ется -функция):


В неко­то­ром смысле, они при этом всё-же раз­личны – кроме струк­тур­ных раз­ли­чий фор­мул, -функцию можно счи­тать при­ми­тивно рекур­сив­ной (для её реа­ли­за­ции не обя­за­тельно нужен уни­вер­саль­ный язык про­грам­ми­ро­ва­ния).

Непо­движ­ные точки вычис­ли­мых функ­ций

Далее опи­сы­ва­ется тео­рема о суще­ство­ва­нии непо­движ­ных точек вычис­ли­мых функ­ций, извест­ная так­же как вто­рая рекур­сив­ная тео­рема Клини [14]; здесь при­во­дится несколько упро­щен­ная её вер­сия, впро­чем, пол­но­стью доста­точ­ная для реше­ния постав­лен­ных задач.

Вто­рая рекур­сив­ная тео­рема Клини.

Для любой вычис­ли­мой функ­ции суще­ствует про­грамма («не­по­движ­ная точ­ка»), такая, что


Дока­за­тель­ство.

Напи­шем про­грамму , такую, что . Тогда .

Дей­стви­тельно, если запу­стим для какого-нибудь аргу­мента , то полу­чим . По опре­де­ле­нию -функции, . Теперь при­ме­ним опре­де­ле­ние для и полу­чим, что . Понятно, что эта цепочка равенств по тран­зи­тив­но­сти при­во­дит к .

В дан­ном сооб­ще­нии осо­бый инте­рес пред­став­ляет част­ный слу­чай , порож­да­ю­щий квайны: легко видеть, что при этом , то есть про­грамма при запуске печа­тает сама себя, как и поло­жено квайну.

Ино­гда при­го­жда­ется дру­гая вер­сия этой тео­ремы (при опре­де­лен­ном выборе языка, строго говоря, не экви­ва­лент­ная тео­реме Кли­ни; см. [39]), пред­ло­жен­ная в [19]:

Тео­рема Род­жерса.

Для любой всюду опре­делён­ной вычис­ли­мой функ­ции суще­ствует непо­движ­ная точка , т.е. такая про­грамма , что .

Дока­за­тель­ство.

Опре­де­лим функ­цию так, что


Теперь напи­шем про­грамму , такую, что выпол­ня­ется

Утвер­жда­ется, что – есть иско­мая непо­движ­ная точка.

Под­ста­новка (3) в пра­вую часть (2) дает и . Послед­нее урав­не­ние при обо­зна­че­нии уже соот­вет­ствует опре­де­ле­нию непо­движ­ной точки из усло­вия этой тео­ремы, т.е., .

N.B., во вто­рой тео­реме Клини о рекур­сии мы можем выбрать любую вычис­ли­мую функ­цию , даже если она не опре­де­лена для неко­то­рых зна­че­ний аргу­мен­тов (т.е. если соот­вет­ству­ю­щая этой функ­ции про­грамма зави­сает при таких вход­ных данных); в тео­реме Род­жерса мы можем выбрать только всюду опре­делён­ную функ­цию , иначе в выра­же­нии не было бы смысла при неко­то­рых .

Из тео­ремы Клини и тео­ремы об уни­вер­саль­ной функ­ции сле­дует тео­рема Род­жерса, а из тео­ремы Род­жерса и -теоремы – тео­рема Клини. (Но есть нюансы [39].)

Для при­ме­не­ния тео­ремы Род­жерса к слу­чаю с квай­нами, удобно, сле­дуя [2], вве­сти опе­ра­тор , для дан­ного аргу­мента кон­стру­и­ру­ю­щий про­грамму, печа­та­ю­щую этот аргу­мент. Т.е. опре­де­ле­ние опе­ра­тора выгля­дит как [и почти соот­вет­ствует одно­имен­ному опе­ра­тору из, e.g., lisp’а].

Квайны суть непо­движ­ные точки этого опе­ра­тора [в смысле фор­му­ли­ровки тео­ремы Род­жерса].

Дока­за­тель­ство.

Пусть – непо­движ­ная точка , т.е. выпол­ня­ется Под­ста­новка опре­де­ле­ния в это урав­не­ние дает . Но и есть опре­де­ле­ние квайна.

При­ме­ча­ние.

Вообще, в мате­ма­тике, непо­движ­ной точ­кой функ­ции обычно назы­вают зна­че­ние , такое, что (i.e., остав­ляет неиз­мен­ным). Поэтому «не­по­движ­ные точ­ки» из тео­рем 1 и 2 не совсем соот­вет­ствуют такому обще­при­ня­тому опре­де­ле­нию.

Тем не менее, тер­мин непо­движ­ная точка хорошо отра­жает суть этих тео­рем – неко­то­рое пре­об­ра­зо­ва­ние не меняет зна­че­ние аргу­мента, т.е. остав­ляет его непо­движ­ным (более того, из сле­дует , i.e. непо­движ­ные точки по Род­жерсу очень близки к обыч­ному опре­де­ле­нию; обрат­ное, впро­чем, неверно: рас­смот­рен­ные тео­ремы о непо­движ­ной точке вовсе не гаран­ти­руют нали­чия обыч­ной непо­движ­ной точки , такой, что ).

Я лишь наме­ре­ва­юсь, в рам­ках этой серии сооб­ще­ний, для удоб­ства вве­сти обо­зна­че­ния для этих «не­стан­дарт­ных» непо­движ­ных точек, а именно, пусть опе­ра­торы и обо­зна­чают непо­движ­ные точки [функ­ции ] из тео­рем Клини и Род­жерса, соот­вет­ственно. В кон­тек­сте, тре­бу­ю­щем работы с несколь­кими непо­движ­ными точ­ками, эти опе­ра­торы будут обо­зна­чать мно­же­ства непо­движ­ных точек. Обо­зна­че­ние будет исполь­зо­ваться для мно­же­ства тра­ди­ци­он­ных непо­движ­ных точек, . А так как, в общем слу­чае, непо­движ­ная точка не явля­ется един­ствен­ной, запись типа пусть озна­чает (и ана­ло­гично для , ).

Финаль­ное заме­ча­ние, пол­но­стью оправ­ды­ва­ю­щее назва­ние «не­по­движ­ная точ­ка» для опе­ра­то­ров и : вве­дем опе­ра­тор , где – неко­то­рое дву­мест­ное отно­ше­ние. Тогда . Дру­гие типы непо­движ­ных точек можно теперь тоже опре­де­лять через при долж­ном выборе отно­ше­ния . Напри­мер, пусть отно­ше­ние срав­ни­вает про­граммы по их эффекту, т.е., . Тогда . (N.B., как обычно, запись есть сокра­ще­ние для ).

Глав­ный экс­пе­ри­мент

В [42] при­ве­ден сле­ду­ю­щий экс­пе­ри­мент по квай­но­ге­не­ра­ции на lisp (автор кода мне не изве­стен):

(define g (quote (lambda (x y) x))) ; g(x, y) = x

(define s11 (quote (lambda (f x) (list (quote lambda)
    (quote (y)) (list f x (quote y)))))) ; from s-m-n theorem

(define m (list (quote lambda) (quote (x y))
    (list g (list s11 (quote x) (quote x)) (quote y))))

(define quine (eval (list s11 m m)))

; tests
(eval (list quine nil))
(eval (list g quine nil))

(В [47] можно найти неве­ро­ят­ную кол­лек­цию ана­ло­гич­ных экс­пе­ри­мен­тов со вто­рой рекур­сив­ный тео­ре­мой Клини, выпол­нен­ных на языке scheme, диа­лекте lisp’а.)

Этот фраг­мент кода про­сто вос­про­из­во­дит дока­за­тель­ство вто­рой рекур­сив­ной тео­ремы Клини. Ана­ло­гич­ным обра­зом я дей­ство­вал и при повто­ре­нии этого экс­пе­ри­мента на sed. Соот­вет­ству­ю­щий код может быть най­ден в GitHub-репо­зи­то­рии [46]. Кон­кретно, в репо­зи­то­рии содер­жится ряд скрип­тов, *.sed и *.sh, назна­че­ние каж­дого из кото­рых рас­смот­ренно ниже.

Редак­тор sed направ­ляет дан­ные со стан­дарт­ного ввода в основ­ной буфер редак­ти­ро­ва­ния (т.н. the pattern space). И т.к. мне не изве­стен спо­соб пере­дачи тра­ди­ци­он­ных аргу­мен­тов команд­ной строки скрипту для этого редак­тора, то именно чте­ние со стан­дарт­ного ввода было при­ме­нено в каче­стве един­ствен­ного меха­низма пере­дачи аргу­мен­тов. При этом, из-за необ­хо­ди­мо­сти пере­дачи несколь­ких аргу­мен­тов, пона­до­би­лось либо вве­сти разделитель/терминатор (e.g. нуле­вой байт), либо при­пи­сы­вать зна­че­ние длины перед каж­дым аргу­мен­том. Как ни странно, изна­чально был выбран вто­рой метод, в основ­ном из-за отсут­ствия необ­хо­ди­мо­сти в экра­ни­ро­ва­нии раз­де­ли­теля.

Итак, далее при­ве­ден пере­чень скрип­тов с их крат­ким опи­са­нием:

(Заметьте, послед­ние два скрипта, first-only.sed и concat.sed кон­цеп­ту­ально не явля­ются лиш­ними и они могли бы быть исполь­зо­ваны, но по-факту, в ходе опти­ми­за­ции, были заме­нены ана­ло­гич­ными сред­ствами команд­ной обо­лочки, – для эффек­тив­но­сти.)

Для гене­ра­ции квайна доста­точно запу­стить ./generate.sh, в резуль­тате чего в том же ката­логе [через неко­то­рое время] дол­жен появиться файл q.sed, явля­ю­щийся иско­мым квай­ном на sed.

Про­ме­жу­точ­ные итоги

Опи­са­ние основ­ного экс­пе­ри­мента завер­шено (и я реко­мен­дую немного поизу­чать код скрип­тов из репо­зи­то­рия). Но о квай­нах рас­ска­зать можно ещё мно­гое. А неко­то­рые вопросы оста­ются вовсе мало­изу­чен­ными, фак­ти­че­ски, откры­тыми.

Несмотря на при­ве­ден­ные дока­за­тель­ста тео­рем о непо­движ­ных точ­ках (Клини и Род­жерса), кому-то, может быть, хоте­лось бы глубже понять струк­туру этих и подоб­ных им дока­за­тельств. Поэтому ниже я при­веду неко­то­рые допол­ни­тель­ные све­де­ния с при­ме­рами. К концу сооб­ще­ния я попро­буя немного задеть неко­то­рые из фило­соф­ских аспек­тов квай­нов и тео­рии вычис­ли­мо­сти. И, пожа­луй, завершу изло­же­ние попыт­кой сфор­му­ли­ро­вать инте­ре­су­ю­щие меня, но пока не име­ю­щие офи­ци­аль­ного ответа откры­тые вопросы.

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

Допол­ни­тель­ная тео­рия

Про­блема оста­новки

Про­бле­мой останова(-ки) назы­вают задачу опре­де­ле­ния завер­ша­е­мо­сти про­из­воль­ной про­граммы по её коду. Известна тео­рема об алго­рит­ми­че­ской нераз­ре­ши­мо­сти такой задачи, т.е. о невоз­мож­но­сти постро­е­ния алго­ритма, уста­нав­ли­ва­ю­щего для про­из­воль­ной про­граммы конеч­ность вре­мени её выпол­не­ния [8].

Дока­за­тель­ство 1.

В пользу этого утвер­жде­ния можно при­ве­сти такие доводы. Вве­дем функ­цию , опре­делён­ную для всех , кото­рая в каче­стве своих аргу­мен­тов при­ни­мает текст неко­то­рой про­граммы и вход­ные дан­ные этой про­граммы, после чего воз­вра­щает 1 если эта про­грамма завер­ша­ется за конеч­ное время, и 0 – если про­грамма «за­ви­са­ет»:


Пусть – любая вычис­ли­мая (реа­ли­зу­е­мая в виде ком­пью­тер­ной про­граммы) функ­ция двух аргу­мен­тов, спо­соб­ная обра­ба­ты­вать тек­сты про­грамм. Напи­шем про­грамму так, чтобы выпол­ня­лось:


Т.е. пере­даёт свой аргу­мент функ­ции и если та воз­вра­щает ноль, то ноль (или любое дру­гое конеч­ное зна­че­ние) воз­вра­щает и , но в про­тив­ном слу­чае воз­вра­щает неопре­де­лен­ное зна­че­ние, а именно зави­сает, входя в бес­ко­неч­ный цикл.

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

Теперь мы можем пере­дать про­грамме её соб­ствен­ный текст; тогда, если , то и . Дру­гими сло­вами, в этом слу­чае воз­вра­щает резуль­тат и завер­ша­ется, т.е. . Если же , то зави­сает и таким обра­зом .

В любом слу­чае, из этого полу­ча­ется, что . Таким обра­зом, функ­ция не сов­па­дает [хотя бы в одной точке] с вычис­ли­мой . А из про­из­воль­но­сти выбора функ­ции немед­ленно выте­кает, что функ­ция не сов­па­дает вообще ни с одной вычис­ли­мой функ­цией, т.е. невы­чис­лима (в смысле несу­ще­ство­ва­ния про­граммы, вычис­ля­ю­щей зна­че­ние этой функ­ции для всех про­грамм).

Дока­за­тель­ство 2.

Инте­ресно, что можно вос­поль­зо­ваться тео­ре­мой Клини и попро­бо­вать дока­зать про­блему оста­нова мето­дом «от про­тив­но­го».

Допу­стим, опре­делён­ная выше функ­ция оста­нова – вычис­ли­мая. Опре­де­лим вычис­ли­мую функ­цию


Вто­рая рекур­сив­ная тео­рема гово­рит, что . Опре­де­ле­ние равен­ства вычис­ли­мых функ­ций, опре­де­ле­ние и опре­де­ле­ние функ­ции оста­нова дают сле­ду­ю­щие цепочки логи­че­ских эква­ва­лент­но­стей

При­шли к про­ти­во­ре­чию; зна­чит, функ­ция не явля­ется вычис­ли­мой.

Диа­го­на­ли­за­ция

Схема пер­вого дока­за­тель­ства для про­блемы оста­нова фак­ти­че­ски осно­вана на про­це­дуре диа­го­на­ли­за­ции [6]. Вто­рое дока­за­тель­ство, впро­чем, тоже: ведь оно исполь­зует тео­рему Клини, дока­за­тель­ство кото­рой так­же осно­вано на диа­го­на­ли­за­ции. Далее я попы­та­юсь пояс­нить суть этого про­цесса. Итак, если у нас есть неко­то­рая бес­ко­неч­ная мат­рица (верх­ний индекс нуме­рует строки, ниж­ний – столбцы), то мы можем постро­ить вектор-строку , не сов­па­да­ю­щий ни с одной из строк мат­рицы. Для этого доста­точно лишь каким-то обра­зом пре­об­ра­зо­вать эле­менты диа­го­нали (отсюда назва­ние метода) мат­рицы так, чтобы для любого выпол­ня­лось (напри­мер, ).

Дей­стви­тельно, пред­по­ло­жим, что равен одной из строк мат­рицы . Это озна­чает, что суще­ствует такой индекс , что для всех . Однако, по-построению , при имеет место нера­вен­ство ; про­ти­во­ре­чие. Сле­до­ва­тельно, мы дей­стви­тельно постро­или новый объ­ект , не вхо­дя­щий в мат­рицу .

При­ме­ча­ние.

Назва­ние метода (диа­го­на­ли­за­ция) несколько условно, т.к., по-видимому, доста­точно чтобы в каж­дой строке был хотя-бы один пре­об­ра­зу­е­мый эле­мент, совсем не обя­за­тельно лежа­щий на глав­ной диа­го­нали. [44] (Правда, нужно учи­ты­вать, что пере­ста­нов­кой строк можно вер­нуть пре­об­ра­зу­е­мые эле­менты на диа­го­наль мат­ри­цы; это заме­ча­ние пока имеет инту­и­тив­ный харак­тер и деталь­ного ана­лиза здесь не будет.)

Воз­можно, что пока при­ме­не­ние этого трюка с кон­стру­и­ро­ва­нием пато­ло­ги­че­ской строки мат­рицы может пока­заться далёким от при­ве­ден­ных дока­за­тельств раз­би­ра­е­мых здесь тео­рем (Клини, Род­жерса и про­блемы остановки); поэтому пред­став­ля­ется нелиш­ним неболь­шое про­дви­же­ние ближе к исто­ри­че­ским осно­вам метода, и, я наде­юсь, это сразу про­яс­нит связь по край­ней мере с про­бле­мой оста­нова. А вот тео­ремы о непо­движ­ных точ­ках потре­буют боль­шего вни­ма­ния… Итак, сей­час коп­нём вглубь исто­рии.

Тео­рема Кан­тора

Изна­чально, диа­го­наль­ный метод был раз­ра­бо­тан и при­ме­нен Кан­то­ром [7] для дока­за­тель­ства суще­ство­ва­ния мно­жеств, боль­ших любого счет­ного бес­ко­неч­ного мно­жества, или, точ­нее говоря, для дока­за­тель­ства несуще­ство­ва­ния сюръ­ек­ции , где – булеан мно­же­ства . Можно выра­зить тео­рему Кан­тора так: . В более общем виде, тео­рема Кан­тора гово­рит о несу­ще­ство­ва­нии сюръ­ек­ции , где , – неко­то­рые мно­же­ства (при­чем должно быть невы­рож­ден­ным, напри­мер в смысле суще­ство­ва­ния бес­по­рядка , т.е., пере­ста­новки без непо­движ­ных точек), а – мно­же­ство всех функ­ций (струк­турно, обо­зна­че­ние соот­вет­ствует фор­муле для под­счета коли­че­ства таких функ­ций).

Рас­смот­рим более детально при­мер с .

Мы можем зако­ди­ро­вать про­из­воль­ное под­мно­же­ство дво­ич­ной мас­кой, т.е. после­до­ва­тель­но­стью , такой, что если и если .

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

Т.е. мы полу­чили совер­шенно новый объ­ект, име­ю­щий кон­крет­ное опи­са­ние в виде дво­ич­ной после­до­ва­тель­но­сти, тоже могу­щей быть дво­ич­ной мас­кой для неко­то­рого под­мно­же­ства , но не вхо­дя­щей в мат­рицу . Этот при­мер пока­зы­вает, что раз строки мат­рицы про­ну­ме­ро­ваны чис­лами , а мы полу­чили «лиш­нюю» дво­ич­ную маску, опре­де­ля­ю­щую некое «лиш­нее» под­мно­же­ство , то мно­же­ство всех под­мно­жеств , содер­жа­щее это лиш­нее под­мно­же­ство и обо­зна­ча­е­мое , строго больше самого ; выра­же­ние «строго боль­ше» озна­чает, что в есть лиш­ние, допол­ни­тель­ные эле­менты и нельзя опре­де­лить какую-нибудь функ­цию из на , т.е. такую функ­цию, чтобы у каж­дого эле­мента был про­об­раз.

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

Тео­рема Ловера

Уильям Ловер пред­ло­жил [43] обоб­щен­ную тео­рему из кото­рой сле­дуют мно­гие дру­гие резуль­таты диа­го­на­ли­за­ции, вклю­чая и про­блему оста­нова и тео­рему Гёделя о непол­ноте и неко­то­рые дру­гие. См. [44] для изло­же­ния тех же резуль­та­тов на языке тео­рии мно­жеств (работа [43] сфор­му­ли­ро­вана с суще­ствен­ным при­вле­че­нием тео­рии кате­го­рий).

Связь диа­го­на­ли­за­ции с тео­ре­мами о непо­движ­ной точке

(Этот под­раз­дел добав­лен через месяцы после раз­ме­ще­ния в моем днев­нике пер­вой вер­сии этого сооб­ще­ния. Доба­вить новый под­раз­дел, демон­стри­ру­ю­щий роль диа­го­на­ли­за­ции при поиске непо­движ­ных точек, я решил после того как од­на­жды про­чи­тал своё же сооб­ще­ние и не уви­дел чёт­ких сле­дов диа­го­наль­ного про­цесса в при­ве­ден­ных дока­за­тель­ствах тео­рем Клини и Род­жерса, кроме харак­тер­ного при­ема с само­при­ме­не­нием про­грамм… Мате­риал в основ­ном напи­сан по моти­вам [56].)

В рас­смот­рен­ных выше при­ме­рах при­ме­не­ния диа­го­наль­ного про­цесса мы опре­де­ляли мно­же­ство всех объ­ек­тов опре­делён­ного типа, пред­став­ляли их стро­ками мат­рицы, а затем при­ме­няли неко­то­рую опе­ра­цию к диа­го­наль­ным эле­мен­там и т.о. кон­стру­и­ро­вали новый объ­ект, не могу­щий быть опи­сан­ным ни одной стро­кой мат­рицы. Но при поиске непо­движ­ной точки, как в тео­ре­мах Клини и Род­жерса, тре­бу­ется, на­обо­рот, постро­ить или найти объ­ект с тре­бу­е­мыми свой­ствами в исход­ном наборе, а не дока­зать, что его там нет. При­чем же тут диа­го­на­ли­за­ция?

Все дело в том, что если нам уда­ется постро­ить новую после­до­ва­тель­ность (ино­гда назы­ва­е­мую анти­диа­го­на­лью [ кото­рую не сле­дует путать с побоч­ной диа­го­на­лью мат­рицы]), путем при­ме­не­ния неко­его пре­об­ра­зо­ва­ния к диа­го­наль­ным эле­мен­там мат­рицы , то мы, как уже было ска­зано ранее, можем быть уве­рены, что среди строк мат­рицы объ­екта нет. Но если по какой-то при­чине «ан­ти­диа­го­наль­ный» объ­ект постро­ить не полу­ча­ется, то среди строк мат­рицы может ока­заться её диа­го­наль. Т.е. воз­можно суще­ствует индекс , такой, что .

Нас инте­ре­сует слу­чай замкну­то­сти набора строк мат­рицы отно­си­тельно дей­ствия : для любой строки суще­ствует строка рав­ная образу , т.е., . Итак, если диа­го­наль мат­рицы равна её строке , то в силу замкну­то­сти строк отно­си­тельно , суще­ствует строка , а так как , то и образ диа­го­нали под дей­ствием тоже ока­зы­ва­ется среди строк: .

Но если резуль­тат пре­об­ра­зо­ва­ния диа­го­нали равен одной из строк, то это озна­чает, что на пере­се­че­нии этой строки с глав­ной диа­го­на­лью мат­рицы есть эле­мент, пере­хо­дя­щий в себя под дей­ствием пре­об­ра­зо­ва­ния . Дру­гими сло­вами, , i.e., – непо­движ­ная точка .

Воз­можно, в визу­а­ли­за­ции этой идеи помо­жет сле­ду­ю­щий рису­нок (мат­рица схе­ма­тично изоб­ра­жена в виде таб­ли­цы; вме­сто каких-либо кон­крет­ных зна­че­ний её ячеек исполь­зу­ется сим­вол ):


Ну вот, связь диа­го­на­ли­за­ции и непо­движ­ных точек в общих чер­тах про­де­мон­стри­ро­вана. Оста­лось выяс­нить, что же может пойти не так при постро­е­нии век­тора . Напомню, что в слу­чае с тео­ре­мой Кан­тора и про­бле­мой оста­нова диа­го­на­ли­за­ция про­хо­дила успеш­но… Раз­гадка здесь всего-лишь в том, что в слу­чае с непо­движ­ными точ­ками мы не строим функ­цию спе­ци­ально так, чтобы у неё не было непо­движ­ных точек. И лишь пред­по­ла­гая, допус­кая их нали­чие, мы при­ме­няем диа­го­наль­ный про­цесс для кон­стру­и­ро­ва­ния такой непо­движ­ной точки. Полу­чив же кон­крет­ное реше­ние, можно легко убе­диться, что это дей­стви­тельно непо­движ­ная точ­ка; с тем же успе­хом, мы могли бы про­сто уга­дать вид реше­ния, а потом убе­диться в его пра­виль­но­сти. Т.о., если отбро­сить вари­ант с уга­ды­ва­нием, то кон­струк­тив­ный харак­тер диа­го­наль­ной схемы суще­стве­нен для её логи­че­ской основы: мы создаём объ­ект с нуж­ными свой­ствами и назад пути уже нет в силу непро­ти­во­ре­чи­во­сти исполь­зу­е­мых фор­маль­ных систем (ведь если выве­дено неко­то­рое утвер­жде­ние, выве­сти его отри­ца­ние в таких систе­мах уже не полу­чится). (Реко­мен­дую так­же гля­нуть инте­рес­ное обсуж­де­ние логи­че­ской основы диа­го­на­ли­за­ции в [57].)

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

По тех­ни­че­ским при­чи­нам, о кото­рых ещё будет ска­зано позже, вме­сто функ­ции мы вве­дем отно­ше­ние (его кон­крет­ный вид будет опре­делён ниже). Вме­сто будем писать ; в част­но­сти, если неко­то­рая функ­ция – иско­мая непо­движ­ная точка, то будет выпол­няться . К стро­кам мат­рицы и вообще к после­до­ва­тель­но­стям это отно­ше­ние при­ме­ня­ется поэле­ментно.

Пусть эле­менты мат­рицы равны . Т.е. строка с индек­сом соот­вет­ствует вычис­ли­мой функ­ции с номе­ром ; номера столб­цов соот­вет­ствуют зна­че­ниям аргу­мен­тов. (Заметьте, что эле­менты мат­рицы равны не самим зна­че­ниям , а вычис­ли­мым функ­циям с номе­рами .) Если для неко­то­рых и , то счи­та­ется, что эле­мент содер­жит нигде не опре­делён­ную функ­цию: . Диа­го­наль обо­зна­чим и будем счи­тать .

Если неко­то­рый гипо­те­ти­че­ский объ­ект , для кото­рого спра­вед­ливо не явля­ется «па­то­ло­ги­че­ским», т.е. гаран­ти­ро­ванно отсут­ству­ю­щим среди строк мат­рицы, то диа­го­наль может ока­заться рав­ной одной из строк.

Мы можем опре­де­лить функ­цию , кон­стру­и­ру­ю­щую про­грамму, кото­рая при­ни­мая аргу­мент , при­ме­няет к нему диа­го­наль­ный эле­мент мат­рицы, нахо­дя­щийся на пере­се­че­нии строки и столбца . А именно, выпол­ня­ется


Важно, что для вычис­ле­ния тоже можно напи­сать про­грамму , т.е. – вычис­ли­мая функ­ция. Это озна­чает, что в мат­рице суще­ствует строка рав­ная ; дей­стви­тельно, из того, что явля­ется про­грам­мой для , а так­же из опре­де­ле­ния эле­мен­тов и из сле­дует . Если набор строк замкнут отно­си­тельно , то суще­ствует строка ; и именно её пере­се­че­ние с дает непо­движ­ную точку: .

Чтобы послед­нее урав­не­ние выгля­дело как урав­не­ние из тео­ремы Род­жерса, опре­де­лим отно­ше­ние так, что для функ­ций и выпол­ня­ется . Попро­буем пока­зать, что . Пусть – такая после­до­ва­тель­ность, что . Из опре­де­ле­ний эле­мен­тов мат­рицы и отно­ше­ния сле­дует и . В силу вычис­ли­мо­сти , ком­по­зи­ция тоже вычис­лима и имеет неко­то­рый индекс , т.е., . В соот­вет­ствии с опре­де­ле­нием эле­мен­тов мат­рицы , это выра­же­ние озна­чает, что равна строке с индек­сом . Т.о., мы только что пока­зали, что набор строк замкнут отно­си­тельно .

Выше было уста­нов­лено, что . Левую часть пере­пи­шем с помо­щью , учи­ты­вая равен­ство , а пра­вую – с помо­щью опре­де­ле­ния эле­мен­тов мат­рицы . Это даёт . Из опре­де­ле­ния отно­ше­ния сле­дует, что . Это урав­не­ние гово­рит, что – один из воз­мож­ных тек­стов про­грамм, вычис­ля­ю­щих ком­по­зи­цию функ­ций ; кроме того, то же самое урав­не­ние вме­сте с в слу­чае с немед­ленно даёт . Таким обра­зом, , где явля­ется про­грам­мой для вычис­ле­ния ком­по­зи­ции , – иско­мая непо­движ­ная точка из тео­ремы Род­жерса, т.е., .

Да, это постро­е­ние выгля­дит более гро­мозд­ким и запу­тан­ным чем изна­чально при­ве­ден­ное почти одно­строч­ное дока­за­тель­ство тео­ремы Род­жерса, но оно, я наде­юсь, поз­во­ляет более глу­боко уви­деть связь диа­го­наль­ного про­цесса с тео­ре­мами о непо­движ­ных точ­ках.

При­ме­ча­ние.

Ранее я гово­рил, что лучше исполь­зо­вать отно­ше­ние вме­сто функ­ции . Чтобы непо­движ­ная точка функ­ции удо­вле­тво­ряла урав­не­нию , можно было бы опре­де­лить как . В прин­ципе, та же схема дока­за­тель­ства рабо­тала бы и в этом слу­чае: сна­чала мы бы выяс­нили, что строки замкнуты отно­си­тельно , затем пре­об­ра­зо­вали бы строку , рав­ную диа­го­нали , и полу­чили бы новую строку ; завер­ши­лось бы такое дока­за­тель­ство про­вер­кой, что эле­мент , нахо­дя­щийся на пере­се­че­нии и , удо­вле­тво­ряет урав­не­нию .

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

Дру­гими сло­вами, из равен­ства должно сле­до­вать , а для этого тре­бу­ется экс­тен­си­о­наль­ность функ­ции – должно выпол­няться , i.e., оди­на­ково веду­щие себя про­граммы, должны пре­об­ра­зо­вы­ваться функ­цией снова в оди­на­ково себя веду­щие, однако не вся­кая вычис­ли­мая функ­ция обла­дает такой фор­мой моно­тон­но­сти. Трюк с заме­ной пре­об­ра­зо­ва­ния (вме­сте с обыч­ным срав­не­нием функ­ций) отно­ше­нием , поз­во­ляет обойти это огра­ни­че­ние и рас­ши­рить класс при­год­ных функ­ций до всех вычис­ли­мых всюду опре­делён­ных.

Вер­сия «от про­тив­но­го».

Рас­суж­де­ния по схеме «а вдруг», могут пока­заться не все­гда удо­вле­тво­ри­тель­ными. Можно ли вме­сто попытки скон­стру­и­ро­вать жела­е­мый объ­ект — непо­движ­ную точку — вопреки диа­го­на­ли­за­ции (при­зван­ной дока­зать невоз­мож­ность подоб­ного меро­при­я­тия) попро­бо­вать обла­чить выше­из­ло­жен­ное в при­выч­ную схему дока­за­тель­ства от про­тив­ного? Такое дока­за­тель­ство выгля­дело бы более при­вычно и ощу­ща­лось бы более весо­мым (ведь дока­за­тель­ство от про­тив­ного осно­вано на надёж­ном про­цессе поиска ошибки в цепочке умо­за­клю­че­ний, при­вед­ших к абсур­ду; такой поиск при­зван, в итоге, лока­ли­зо­вать ошибку, загнав её в един­ствен­ный туман­ный уго­лок дока­за­тель­ства, а именно, в самое его началь­ное пред­по­ло­же­ние, после чего спо­соб исправ­ле­ния ошибки — отри­ца­нием пред­по­ло­же­ния — немед­ленно сле­дует из закона исклю­чён­ного тре­тьего, и, таким обра­зом любые пре­тен­зии к каче­ству такого дока­за­тель­ства сво­дятся к вере в этот закон). Ока­зы­ва­ется, что, да, дока­за­тель­ство, одно­вре­менно явно исполь­зу­ю­щее диа­го­на­ли­за­цию и пока­зы­ва­ю­щее нали­чие непо­движ­ной точки мето­дом от про­тив­ного, суще­ствует, и, в общем-то, обще­из­вестно.

Итак. Опре­де­лим бинар­ное отно­ше­ние так, что .

Тео­рема (пере­фор­му­ли­ровка тео­ремы Род­жерса).

Если – всюду опре­делён­ная вычис­ли­мая функ­ция, то .

Дока­за­тель­ство тео­ремы Род­жерса мето­дом от про­тив­ного.

(N.B., Акцент в этой вер­сии дока­за­тель­ства сде­лан на диа­го­на­ли­за­ции, пусть и в ущерб кон­струк­тив­но­сти, — здесь я не буду выво­дить явный вид непо­движ­ной точки, хотя при жела­нии его можно извлечь и из такой уре­зан­ной вер­сии.)

Основ­ное пред­по­ло­же­ние: .

Лемма 1 (без дока­за­тель­ства). Для любой вычис­ли­мой функ­ции суще­ствует вычис­ли­мая всюду опре­делён­ная (т.н., -продолжение функ­ции ), такая, что .

Опре­де­лим вычис­ли­мую всюду опре­делён­ную функ­цию , такую, что .

Утвер­жде­ние 1. Не суще­ствует вычис­ли­мой функ­ции , такой, что (иначе это бы про­ти­во­ре­чило опре­де­ле­нию уни­вер­саль­ной функ­ции ).

(N.B., кон­крет­ный вид на самом деле не важен, — тре­бу­ется лишь спра­вед­ли­вость утвер­жде­ния 1, — но здесь мы всё-таки зафик­си­руем такое «диа­го­наль­ное» выра­же­ние, и даже вос­поль­зу­емся им в конце этого под­раз­дела для луч­шей визу­а­ли­за­ции про­цесса.)

По лемме 1, , т.е., . Опре­де­лим функ­цию . Т.к. и и их ком­по­зи­ция – вычис­лима. Допу­стим, . Это вме­сте с и с опре­де­ле­нием отно­ше­ния ведет к , из чего сле­дует . Но , откуда , однако по основ­ному пред­по­ло­же­нию, ( выгля­дит как непо­движ­ная точка, но мы пред­по­ла­гаем, что их не суще­ствует). Про­ти­во­ре­чие, зна­чит, отли­ча­ется всюду от . Но это про­ти­во­ре­чит уже утвер­жде­нию 1. Сле­до­ва­тельно, основ­ное пред­по­ло­же­ние неверно и мы должны его испра­вить, заме­нив на .

Обра­тите вни­ма­ние, здесь мы имеем дело с мат­ри­цей в кото­рой строки, как и преж­де, соот­вет­ствуют вычис­ли­мым функ­циям, а столбцы – их аргу­мен­там (изме­ни­лись только сами эле­менты мат­рицы). Функ­ция порож­дена диа­го­на­лью . Лемма 1 гово­рит, что в мат­рице при­сут­ствует строка (отно­ше­ние при­ме­нено поэле­ментно). Мы исполь­зуем эту строку для кон­стру­и­ро­ва­ния кан­ди­дата в анти­диа­го­наль, а именно мы дей­ствуем на дан­ной вычис­ли­мой функ­цией , но т.к. строка соот­вет­ствует функ­ции , а ком­по­зи­ция вычис­лима, то среди строк должна най­тись строка (пере­се­ка­ю­щая диа­го­наль в неко­то­рой точке).

Откуда же тогда берутся подо­зре­ния в анти­диа­го­наль­но­сти , т.е. в том, что ? Мы знаем, что (так мы стро­или строку ). Теперь если для какого-то верно , то в строке най­дётся эле­мент , но по опре­де­ле­нию эле­мен­тов мат­рицы , , откуда и выте­кает выра­же­ние , про­ти­во­ре­ча­щее основ­ному пред­по­ло­же­нию [о несу­ще­ство­ва­нии непо­движ­ной точки ], а зна­чит опро­вер­га­ю­щее, мето­дом от про­тив­ного, наше пред­по­ло­же­ние о том, что сов­па­дает с в точке .

На дан­ный момент мы имеем уже два утвер­жде­ния: одно гово­рит о том, что анти­диа­го­наль на самом деле равна строке и пере­се­ка­ется с диа­го­на­лью в неко­то­рой точке, а вто­рое утвер­жде­ние гово­рит, что анти­диа­го­наль дей­стви­тельно нигде не сов­па­дает с диа­го­на­лью. Эти утвер­жде­ния про­ти­во­ре­чат друг другу, что абсурдно, и, опять мето­дом от про­тив­ного, при­во­дит к утвер­жде­нию, прямо про­ти­во­по­лож­ному основ­ному пред­по­ло­же­нию об отсут­ствии непо­движ­ных точек у дан­ной функ­ции .

Я спе­ци­ально вос­про­из­вел послед­нее дока­за­тель­ство от про­тив­ного в уже зна­ко­мых тер­ми­нах мат­риц, диа­го­на­лей, строк, антидиа­го­на­лей, и т.д., чтобы при­дать мак­си­маль­ную нагляд­ность про­цессу и окон­ча­тельно уда­лить мисти­че­ский налёт с диа­го­наль­ного дока­за­тель­ства тео­ремы о рекур­сии.

Немного о пре­де­лах позна­ва­е­мо­сти

Про­блема оста­нова раз­ре­шима для мно­гих систем, напри­мер, для конеч­ных авто­ма­тов, коими, кстати говоря, и явля­ются реаль­ные ком­пью­теры. Правда, в силу огром­ного коли­че­ства состо­я­ний таких авто­ма­тов, они, с прак­ти­че­ской точки зре­ния, хорошо моде­ли­ру­ются маши­ной Тью­ринга (или экви­ва­лент­ными моде­лями, типа лямбда-исчисления). Поэтому, мно­гие тео­ре­ти­че­ские резуль­таты, выте­ка­ю­щие из про­блемы оста­нова или дока­зы­ва­е­мые редук­цией к ней, по преж­нему при­ме­нимы в обыч­ном про­грам­ми­ро­ва­нии.

Так­же про­блема оста­нова может быть вполне раз­ре­ши­мой для опре­де­лен­ных част­ных слу­чаев. Напри­мер, в тео­рии, анти­ви­русы невоз­можны (это дока­зы­ва­ется редук­цией к про­блеме оста­новки), но они всё-таки вер­тятся суще­ствуют как про­грамм­ные про­дукты (в том числе, бла­го­даря «мар­ке­тин­гу», навер­ное). В тео­рии, невоз­можно напи­сать программу-детек­тор, без­оши­бочно опре­де­ля­ю­щую, что дру­гая про­грамма печа­тает на экране, напри­мер, слово «при­вет» [28, 29]. Но ведь мно­гие бы без труда напи­сали такой детек­тор. Даже сами дока­за­тель­ства тео­рем, в силу соот­вет­ствия Карри-Ховарда, вообще говоря, тре­буют реше­ния про­блемы оста­новки. Но ведь мате­ма­тики как-то дока­зы­вают тео­ремы! Ино­гда при помощи ком­пью­тера (и авто­ма­ти­зи­ро­ванно и даже авто­ма­ти­че­ски).

Эти исклю­че­ния не явля­ются ошиб­ками или фак­тами, опро­вер­га­ю­щими упо­мя­ну­тые кон­трин­ту­и­тив­ные утвер­жде­ния (содер­жа­щие кван­тор все­общ­но­сти). А поло­жи­тель­ные резуль­таты имеют вполне кон­крет­ное при­ме­не­ние. Ком­пи­ля­торы, интер­пре­та­торы, квайны и мно­гие дру­гие инте­рес­ные арте­факты тео­рии вычис­ли­мо­сти суще­ствуют не только тео­ре­ти­че­ски, но и имеют ося­за­е­мые реа­ли­за­ции в виде реаль­ных кус­ков кода и даже устройств (e.g. те же ком­пью­теры). А тео­рема Гёделя о непол­ноте гово­рит, что неко­то­рые тео­ремы про­сто не могут быть дока­заны, – не стоит даже и пытаться.

Нако­нец, диа­го­на­ли­за­ция внесла неко­то­рый вклад и в более абстракт­ные обла­сти фило­со­фии. Напри­мер, при­ме­не­нием диа­го­наль­ного про­цесса к демону Лапласа — «древ­не­му» супер­ком­пью­теру, име­ю­щему неогра­ни­чен­ный доступ к любой инфор­ма­ции обо всех части­цах все­лен­ной — была уста­нов­лена «мо­но­те­и­сти­че­ская» тео­рема о невоз­мож­но­сти суще­ство­ва­ния более чем одного демона Лапласа [27].

В сле­ду­ю­щих сооб­ще­ниях этой серии я напишу о неко­то­рых из этих [как клас­си­че­ских, так и отно­си­тельно новых] резуль­та­тов подроб­нее.

Дру­гие под­ходы и «слегка откры­тые» вопросы

В этом раз­деле обсуж­да­ются доста­точно про­стые, но до сих пор пред­став­ля­ю­щие неко­то­рый иссле­до­ва­тель­ский инте­рес аль­тер­на­тив­ные под­ходы к кон­стру­и­ро­ва­нию квай­нов. Неко­то­рые из рас­смот­рен­ных здесь вопро­сов могут быть три­ви­аль­ными и, воз­можно, пред­став­ля­ю­щими слож­ность лишь для меня; дру­гие – могут ока­заться объ­ек­тивно сто­я­щими даль­ней­шей про­ра­ботки.

Более подробно все эти воросы будет или не будут осве­щены в следующем(-их) сообщении(-ях) этой серии.

«Кре­а­ци­а­низм» vs. «абио­ге­нез»

Я условно назвал сово­куп­ность мето­дов (как руч­ных, так и авто­ма­ти­че­ских), при­ме­ня­е­мых для кон­стру­и­ро­ва­ния квай­нов и осно­ван­ных на суще­ствен­ном при­вне­се­нии спе­ци­ально под­го­тов­лен­ной внеш­ней информации/знаний в этот про­цесс [квай­но­ло­ги­че­ским] «кре­а­ци­а­низ­мом». Тра­ди­ци­он­ное напи­са­ние квайна вруч­ную, а так­же его кон­стру­и­ро­ва­ния на основе вто­рой рекур­сив­ной тео­ремы Клини – суть при­меры кре­а­ци­а­нист­кого под­хода в квай­но­ло­гии. В них тре­бу­ется суще­ствен­ный кон­троль со сто­роны про­грам­ми­ста.

В под­раз­де­лах же, при­ве­ден­ных ниже, акцент сде­лан на авто­ма­ти­че­ском ите­ра­тив­ном кон­стру­и­ро­ва­нии квай­нов (если оно воз­можно). «Абио­ге­не­ти­че­ское», само­сто­я­тель­ное «за­рож­де­ние» квай­нов потен­ци­ально могло бы пред­став­лять неко­то­рую цен­ность в ряде обла­стей. Так, напри­мер, квай­но­ло­ги­че­ский абио­ге­нез был бы инте­ре­сен в таком раз­деле инфор­ма­ци­он­ной без­опас­но­сти как ком­пью­тер­ная виру­со­ло­гия; он был бы при­ме­ним в авто­ном­ных систе­мах (e.g., само­мо­ди­фи­ци­ру­ю­ще­еся ПО каких-нибудь мар­со­хо­дов) и, воз­можно, инте­ре­сен экзо­био­ло­гам (в том смысле, что квайны, будучи «ди­стил­ли­ро­ван­ной» моде­лью жизни, подо­шли бы для изу­че­ния тва­рей с terra incognita, – будь-то оке­ан­ские впа­дины, асте­ро­иды, кос­ми­че­ская пыль/мусор, или любые дру­гие суб­страты).

Кими­ан­ские квайны и ите­ра­тив­ное вычис­ле­ние непо­движ­ной точки

Хофштад­тер в [26] не только ввел тер­мин «квайн», но и опи­сал там же ещё одну раз­но­вид­ность само­реп­ли­ци­ру­ю­ще­гося кода – кими­ан­ские квайны, назван­ные в честь Скотта Кима (Scott Kim), под­ска­зав­шего эту идею Хофштад­теру.

Кими­ан­ский квайн пред­став­ляет собой текст, кото­рый будучи пода­ным на вход компилятора/интерпретатора, не рас­по­зна­ется как кор­рект­ный исход­ный текст, а при­во­дит к выводу сооб­ще­ния об ошибке [31]. При­чём такое сооб­ще­ние побук­венно сов­па­дает с самим кими­ан­ским квай­ном. Т.е. при попытке запуска или ком­пи­ля­ции, подоб­ный код всё-таки вос­про­из­во­дит себя, как и поло­жено квайну.

Если обо­зна­чить среду испол­не­ния для кими­ан­ского квайна (e.g., ком­пи­ля­тор, интер­пре­та­тор или команд­ную обо­лочку) как функ­цию , при­ни­ма­ю­щую и воз­вра­ща­ю­щую тек­сто­вую строку, то неко­то­рый кими­ан­ский квайн будет непо­движ­ной точ­кой этой функ­ции: .

Инте­ресно, что в дан­ном слу­чае может быть вычис­лен ите­ри­ро­ва­нием функ­ции начи­ная с под­хо­дя­щего (ино­гда пустого) началь­ного при­бли­же­ния. Т.е. алго­ритм кон­стру­и­ро­ва­ния кими­ан­ского квайна дол­жен будет вычис­лить , где , для неко­то­рого началь­ного зна­че­ния .

Сле­дует заме­тить, что такая функ­ция опре­де­лена для всех (и к тому же все­гда завер­ша­ется, если речь идет о ком­пи­ля­торе без под­держки вычислительно-универсальных мета­линг­ви­сти­че­ских средств, — напри­мер, подоб­ных C++ шаб­ло­нам, — могу­щих при­во­дить к «за­ви­са­нию» ком­пи­ля­тора).

В мини­ре­по­зи­то­рии (gist) [33] можно найти про­стую реа­ли­за­цию подоб­ного метода ите­ра­тив­ного кон­стру­и­ро­ва­ния кими­ан­ского квайна, выпол­нен­ную на языке команд­ной обо­лочки bash. Скрипт при­ни­мает в каче­стве аргу­мента путь до испол­ня­е­мого файла интер­пре­та­тора или ком­пи­ля­тора, запус­кает его и пере­дает ему имя файла, хра­ня­щего началь­ное при­бли­же­ние. Резуль­тат из пото­ков вывода сохра­ня­ется в этот же файл если этот резуль­тат отли­ча­ется от содер­жи­мого файла, после чего про­цесс повто­ря­ется. И так до ста­би­ли­за­ции про­цесса, т.е. пока дан­ный интер­пре­та­тор не будет выда­вать тот же текст, кото­рый он полу­чил на вход.

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

Обсуж­да­е­мый скрипт [33] имеют руди­мен­тар­ную под­держку опре­де­ле­ния таких цик­лов, но для меня оста­ется откры­тым вопрос о воз­мож­но­сти авто­ма­ти­че­ского «пре­об­ра­зо­ва­ния» такого цикла в насто­я­щую непо­движ­ную точку (фор­мально, в цикл из одного эле­мента).

(Несмотря на наблю­да­е­мую эмпи­ри­че­скую схо­ди­мость ите­ра­ций к непо­движ­ной точке или к циклу, мне неиз­вестны какие-либо осо­бые свой­ства функ­ции , гаран­ти­ру­ю­щие ста­би­ли­за­цию про­цесса после конеч­ного коли­че­ства шагов.)

Дру­гие кон­струк­тив­ные тео­ремы о непо­движ­ной точке

Пожа­луй, это самый спе­ку­ля­тив­ный и нефор­маль­ный под­раз­дел этого сооб­ще­ния. Здесь пере­чис­ля­ются неко­то­рые извест­ные тео­ремы о непо­движ­ных точ­ках, кото­рые, воз­можно, могли бы быть при­ме­нены для кон­стру­и­ро­ва­ния квай­нов. Сама эта воз­можность нахо­дится под вопро­сом, но эти тео­ремы всё-же заслу­жи­вают упо­ми­на­ния в таком кон­тек­сте.

Пер­вая рекур­сив­ная тео­рема Клини

(Этот раз­дел напи­сан по моти­вам соот­вет­ству­ю­щего мате­ри­ала из [50].)

Для функ­ций и , таких, что , и вве­дем обо­зна­че­ние , чита­ю­ще­еся как « про­дол­жает ». (Дру­гими сло­вами, выпол­ня­ется импли­ка­ция .)

Функ­ция назы­ва­ется конеч­ной если она имеет конеч­ную область опре­де­ле­ния. Конеч­ной частью функ­ции назо­вём конеч­ную функ­цию . Сопо­ста­вим функ­ции нату­раль­ное число .

Под­ра­зу­ме­ва­ется, что суще­ствует алго­ритм, кото­рый по чис­лам и вычис­ляет если суще­ствует , такая, что и опре­де­лена в точке .

Опе­ра­тор назы­ва­ется рекур­сив­ным опе­ра­то­ром если суще­ствует вычис­ли­мая функ­ция , такая, что тогда и только тогда, когда суще­ствует конеч­ная , такая, что .

Опе­ра­тор непре­ры­вен если тогда и только тогда, когда суще­ствует конеч­ная , такая, что .

Опе­ра­тор назы­вают моно­тон­ным, если .

Утвер­жде­ние (без дока­за­тель­ства).

Рекур­сив­ные опе­ра­торы непре­рывны и моно­тонны.

Тео­рема (без дока­за­тель­ства).

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

Для демон­стра­ции связи с квай­нами в [50] (и это прак­ти­че­ски един­ствен­ная обна­ру­жен­ная мной в лите­ра­туре зацепка, свя­зы­ва­ю­щая первую рекур­сив­ную тео­рему с квай­нами) по-сути пред­ла­га­ется попро­бо­вать в каче­стве рекур­сив­ного опе­ра­тора взять опе­ра­тор, ана­ло­гич­ный опе­ра­тору , опре­де­лен­ному в раз­деле о тео­реме Род­жерса (см. выше). Опе­ра­тор пре­об­ра­зует про­грамму в про­грамму, печа­та­ю­щую текст , а по только что изло­жен­ной пер­вой рекур­сив­ной тео­реме Клини, рекур­сив­ный опе­ра­тор (с семан­ти­кой опе­ра­тора ) дол­жен иметь непо­движ­ную точку и если из этого сле­дует, что и сам опе­ра­тор имеет непо­движ­ную точку , такую, что , то, коль скоро про­грамма равна про­грамме, печа­та­ю­щей себя, есть квайн.

Здесь уместно про­ком­мен­ти­ро­вать согла­со­ван­ность этого вывода с дан­ным выше опре­де­ле­нием рекур­сив­ного опе­ра­тора, ведь дей­ствует не на мно­же­стве вычис­ли­мых функ­ций, а на мно­же­стве исход­ных тек­стов про­грамм. Идея состоит в том, что опе­ра­тор , пре­об­ра­зуя неко­то­рую про­грамму в про­грамму , порож­дает рекур­сив­ный опе­ра­тор , пре­об­ра­зу­ю­щий функ­цию в функ­цию . Обо­зна­чим , тогда выше­опи­сан­ную идею можно про­ил­лю­стри­ро­вать сле­ду­ю­щей диа­грам­мой:


К опе­ра­тору напря­мую при­ме­нима пер­вая рекур­сив­ная тео­рема, т.е. суще­ствует , такая, что . Из этого сле­дует, что суще­ствуют про­граммы и , при­чем


Т.к., и , то

Под­став­ляя (4) в (5) и при­ме­няя опре­де­ле­ние опе­ра­тора полу­чаем , т.е. удо­вле­тво­ряет «урав­не­нию квай­на» и, соот­вет­ственно, явля­ется квай­ном.

(Для меня оста­ется откры­тым вопрос о воз­мож­но­сти при­ме­не­ния пер­вой рекур­сив­ной тео­ремы для авто­ма­ти­че­ского кон­стру­и­ро­ва­ния квай­нов, как в слу­чае со вто­рой рекур­сив­ной тео­ре­мой.)

Тео­рема Клини о непо­движ­ной точке [из тео­рии решёток]

Про­из­воль­ное отоб­ра­же­ние , непре­рыв­ное по Скотту и опре­де­лен­ное на пол­ном частично упо­ря­до­чен­ном мно­же­стве с отно­ше­нием порядка имеет наи­мень­шую непо­движ­ную точку.

Непре­рыв­ность по Скотту озна­чает суще­ство­ва­ние и выпол­не­ние для вся­кого направ­лен­ного под­мно­же­ства .

Тео­рема Кнастера-Тарского

В [37] при­ве­дена тео­рема о суще­ство­ва­нии непо­движ­ных точек моно­тон­ных отоб­ра­же­ний на пол­ной решетке. Пусть – моно­тон­ное отоб­ра­же­ние на пол­ной решетке с отно­ше­нием частич­ного порядка . Т.е. . Тео­рема Тар­ского утвер­ждает, что , i.e. мно­же­ство непо­движ­ных точек отоб­ра­же­ния , само явля­ется непу­стой пол­ной решет­кой.

В [38] дока­зана кон­струк­тив­ная вер­сия этой тео­ремы. До этой работы, непо­движ­ные точки моно­тон­ных отоб­ра­же­ний уже кон­стру­и­ро­вали как , где , а . Но это тре­бо­вало от непре­рыв­но­сти по Скотту. В [38] же дока­зы­ва­ется тео­рема, кото­рая не тре­бует от этого свой­ства и утвер­ждая, что явля­ется пол­ной решет­кой отно­си­тельно , пред­ла­гает, среди про­чего, рецепт вычис­ле­ния непо­движ­ной точки в виде .

Здесь опре­де­ля­ется как пре­дел ста­ци­о­нар­ной после­до­ва­тель­но­сти при . Счи­та­ется, что для пре­дель­ного орди­нала . Под ста­ци­о­нар­но­стью пони­ма­ется, что начи­ная с неко­торго индекса, эле­менты после­до­ва­тель­но­сти имеют одно и то же зна­че­ние. Для кор­рект­ного погру­же­ния в исполь­зо­ван­ные в [38] транс­фи­нит­ные опре­де­ле­ния необ­хо­димо чте­ние ори­ги­наль­ной ста­тьи.

(Эти резуль­таты обоб­ща­ются на слу­чай пол­ных частично упо­ря­до­чен­ных мно­жеств, только направ­лен­ные, а не про­из­воль­ные подмно­жества кото­рых обя­заны иметь супре­мум.)

Ком­би­на­тор непо­движ­ной точки в лямбда-исчислении

В -исчислении [34] для реа­ли­за­ции рекур­сии исполь­зу­ются ком­би­на­торы непо­движ­ной точки, в основ­ном -комбинатор. Этот ком­би­на­тор опре­де­ля­ется урав­не­нием и в соот­вет­ствии с этим опре­де­ле­нием вычис­ляет непо­движ­ную точку функ­ции , т.е. такое выра­же­ние , что .

Если кон­крет­ная реа­ли­за­ция -исчисления под­дер­жи­вает рекур­сив­ные опре­де­ле­ния име­но­ван­ных сим­во­лов, то выше­при­ве­ден­ного опре­де­ле­ния доста­точно для работы с -комбинатором (могут воз­ра­зить, что в таком слу­чае он не особо-то и нужен; однако, здесь -комбинатор может слу­жить полез­ной обёрт­кой для неко­то­рых функ­ций, поз­во­ля­ю­щей, к при­меру, доба­вить вре­мен­ное хра­не­ние про­ме­жу­точ­ных резуль­та­тов с целью исклю­че­ния их повтор­ного вычис­ле­ния, — т.е., кэши­ро­ва­ние, — и, соот­вет­ственно, полу­чить уско­ре­ние в духе дина­ми­че­ского про­грам­ми­ро­ва­ния).

В чистом же -исчислении тре­бу­ется опре­де­лить -комбинатор в явном виде, напри­мер как (извест­ный как ком­би­на­тор Карри или пара­док­саль­ный ком­би­на­тор) или, в слу­чае исполь­зо­ва­ния импе­ра­тив­ного языка, задей­ство­вать [энер­гич­ный] ком­би­на­тор Тью­ринга , где .

Тео­рема о непо­движ­ной точке.

Несмотря на неоче­вид­ность этого утвер­жде­ния, в нети­пи­зи­ро­ван­ном -исчислении любое выра­же­ние имеет непо­движ­ную точку (хотя бы одну).

Дока­за­тель­ство.

Пусть . Ком­би­на­тор Тью­ринга есть . Утвер­жда­ется, что для любого -выражения , выра­же­ние есть его непо­движ­ная точка. Чтобы уви­деть это, запи­шем сле­ду­ю­щую цепочку урав­не­ний:



где обо­зна­чает после­до­ва­тель­ность из нуля или несколь­ких шагов -редукции.

Теперь -комбинатор может быть при­ме­нен для вычис­ле­ния зна­че­ний функ­ций, вызы­ва­ю­щих сами себя, в среде, не име­ю­щей воз­мож­но­сти выпол­нять име­но­ван­ный рекур­сив­ный вызов или вообще не под­дер­жи­ва­ю­щей име­но­ва­ние объ­ек­тов.

Напри­мер, если тре­бу­ется вычис­лить функ­цию , вызы­ва­ю­щую саму себя по имени , то можно опре­де­лить новую функ­цию , при­ни­ма­ю­щую как аргу­мент: (а при исполь­зо­ва­нии индек­сов де Брёйна или бинар­ного лямбда-исчисления мы можем изба­виться и от име­но­ван­ных пере­мен­ных вовсе). После опре­де­ле­ния мы можем вычис­лить как , потому что есть непо­движ­ная точка , т.е., , а , как было ска­зано выше, как раз и нахо­дит такую непо­движ­ную точку. Жела­ю­щие могут непо­сред­ствен­ной под­ста­нов­кой убе­диться, что в этом слу­чае дей­стви­тельно выпол­ня­ется .

Учи­ты­вая уни­вер­саль­ность -исчисления и выше­при­ве­ден­ную тео­рему о непо­движ­ной точке [в -исчислении], есте­ственно воз­ни­кает идея об исполь­зо­ва­нии ком­би­на­тора непо­движ­ной точки для кон­стру­и­ро­ва­ния квай­нов. (См. так­же [2, 47].)

(Утвер­жде­ние о суще­ство­ва­нии ком­би­на­тора непо­движ­ной точки прак­ти­че­ски экви­ва­лентно выше­при­ве­ден­ный пер­вой рекур­сив­ной тео­реме Клини, но выгля­дит более кон­крет­ным.)

Логи­че­ское про­грам­ми­ро­ва­ние в огра­ни­че­ниях

В [12, 49] при­во­дится инте­рес­ный метод гене­ра­ции квай­нов с исполь­зо­ва­нием логи­че­ского про­грам­ми­ро­ва­ния. Авторы исполь­зо­вали язык логи­че­ского про­грам­ми­ро­ва­ния miniKanren [52], поз­во­ля­ю­щий напи­сать на нём ана­лог «ос­нов­ного урав­не­ния квай­нов» и дать системе решить его отно­си­тельно

С исполь­зо­ва­нием реа­ли­за­ции miniKanren, выпол­нен­ной на языке scheme, квайны можно гене­ри­ро­вать при­мерно так (q.scm взят из [49]):

(load "q.scm")
(run 1 (q) (eval-expo q '() q)))

Квайны вычис­ля­ются быстро (за секунду) и имеют малые раз­меры.

(Суще­ствует мно­же­ство реа­ли­за­ций miniKanren и на дру­гих язы­ках, e.g., на ECMAScript/JavaScript, Python, Ruby, etc.)

Эво­лю­ци­он­ные алго­ритмы

Крайне инте­рес­ной пред­став­ля­ется потен­ци­аль­ная воз­мож­ность гене­ра­ции квай­нов с помо­щью гене­ти­че­ских алго­рит­мов. Среди мно­гих про­блем, воз­ни­ка­ю­щих при гене­ра­ции про­грамм эво­лю­ци­он­ными мето­дами, выде­ля­ется про­блема сохра­не­ния допу­сти­мо­сти или син­так­си­че­ской «кор­рект­но­сти» про­грамм при дей­ствии гене­ти­че­ских опе­ра­то­ров (крос­со­вер, мута­ция).

В этом смысле, одним из наи­бо­лее при­год­ных пред­став­ле­ний для инкре­мент­ной эво­лю­ци­он­ной гене­ра­ции, явля­ются искус­ствен­ные ней­рон­ные сети (далее ИНС или ней­ро­сети). Будучи сетями одно­тип­ных нели­ней­ных сум­ма­то­ров, работа кото­рых зави­сит от весов свя­зей (синап­сов) между ними, ней­ро­сети про­дол­жают сохра­нять рабо­то­спо­соб­ность даже при почти слу­чай­ных изменениях/«повреждениях» в мат­рице их весо­вых коэф­фи­ци­ен­тов. Это довольно сильно кон­тра­сти­рует с тра­ди­ци­он­ными язы­ками про­грам­ми­ро­ва­ния, про­граммы на кото­рых с гораздо боль­шей веро­ят­но­стью под­вер­жены выходу из строя при малей­ших необ­ду­ман­ных моди­фи­ка­циях их исход­ного тек­ста.

Квайны в виде ней­ро­се­тей уже известны. В [41] опи­сана ИНС, обу­ча­ю­ща­яся выво­дить зна­че­ния своих же синап­ти­че­ских коэф­фи­ци­ен­тов. Я же хотел бы здесь сосре­до­то­чится на дру­гом спо­собе гене­ра­ции квай­нов, а именно на кон­стру­и­ро­ва­нии про­грамм посред­ством при­ме­не­ния гене­ти­че­ских алго­рит­мов к про­ме­жу­точ­ному пред­став­ле­нию на основе ней­ро­се­тей.

Есть инте­рес­ный экс­пе­ри­мен­таль­ный язык Anne, [40], при­ду­ман­ный спе­ци­ально для работы с ней­ро­се­тями и транс­ля­тор neuralbf [16, 51] того же автора, пре­об­ра­зу­ю­щий про­грамму на bf в опи­са­ние рекур­рент­ной ИНС. Послед­няя работа (кому-то могу­щая пока­заться несерь­ёз­ной) фоку­си­ру­ется на при­ме­не­нии гене­ти­че­ских алго­рит­мов для совер­шен­ство­ва­ния и исправ­ле­ния уже напи­сан­ных вруч­ную про­грамм (вме­сто их гене­ра­ции без началь­ного при­бли­же­ния).

Цеп­ные квайны

Назо­вём цеп­ным квай­ном (или ите­ра­тив­ным квай­ном) с пери­о­дом , после­до­ва­тель­ность про­грамм , такую, что и .

Можно полу­чить инте­рес­ное обоб­ще­ние — мно­го­язы­ко­вой или «ре­лей­ный» квайн — если заме­нить в только-что дан­ном опре­де­ле­нии скобки на , где – воз­можно раз­лич­ные языки про­грам­ми­ро­ва­ния (под­ра­зу­ме­ва­ется ).

Тео­рема (без дока­за­тель­ства).

Цеп­ные квайны суще­ствуют для любого пери­ода .

Вся­кий, кто пытался писать квайны и цеп­ные квайны, не мог не отме­тить неко­то­рое отли­чие в слож­но­сти их напи­са­ния – цеп­ные квайны [при ] обычно проще истин­ных квай­нов (i.e., тоже цеп­ных квай­нов, но с пери­о­дом ).

Соот­вет­ственно, не может не воз­ник­нуть вопрос о реа­ли­зу­е­мо­сти пол­но­стью авто­ма­ти­че­ского пре­об­ра­зо­ва­ния дан­ной после­до­ва­тель­но­сти в насто­я­щий квайн .

Гене­ра­тор квай­нов, не зави­си­мый от языка

В раз­деле о кон­стру­и­ро­ва­нии квай­нов на основе вто­рой рекур­сив­ной тео­ремы Клини, при­во­дился гото­вый рецепт, алго­ритм гене­ра­ции квайна для кон­крет­ного зара­нее выбран­ного языка про­грам­ми­ро­ва­ния. (Похо­жий алго­ритм воз­мо­жен и в кон­тек­сте дока­за­тель­ства тео­ремы Род­жерса.) Воз­можно, что слово «алго­ритм» здесь можно пони­мать более фор­мально и бук­вально.

Пред­став­ля­ется инте­рес­ным вопрос о суще­ство­ва­нии про­граммы , — «квайн­ти­фи­ка­то­ра», — гене­ри­ру­ю­щей квайн на ука­зан­ном языке с исполь­зо­ва­нием неко­то­рого коли­че­ства образ­цов кода на этом языке. При­чем и разум­ной спе­ци­фи­ка­цией языка и одно­вре­менно образ­цом кода на нём может слу­жить интер­пре­та­тор языка (суще­ству­ю­щий по тео­реме об уни­вер­саль­ной функ­ции).

Чуть более фор­мально задача может быть постав­лена так. Пусть дан интер­пре­та­тор языка , напи­сан­ный на своем же вход­ном языке, т.е. для любой про­граммы на языка выпол­ня­ется . Пусть так­же оттранс­ли­ро­ван на рефе­ренс­ный язык (семан­ти­че­ские скобки без индекса) и суще­ствует на нём в виде про­граммы . В этом слу­чае спра­вед­ливо .

Спра­ши­ва­ется, как может выгля­деть про­грамма , такая, что и ? Выше мы видели, что есть непо­движ­ная точка (по Род­жерсу) опе­ра­тора . При попытке реа­ли­зо­вать выяс­ня­ется, что струк­тура соот­вет­ству­ю­щей про­граммы сильно схожа со струк­ту­рой интер­пре­та­тора. Воз­можно, это сход­ство можно исполь­зо­вать для напи­са­ния Но это тре­бует даль­ней­шего обду­мы­ва­ния.

Вме­сто само­ин­тер­пре­та­то­ров можно исполь­зо­вать обычно более про­стые про­граммы, — спе­ци­а­ли­за­торы, — реа­ли­зу­ю­щие -функцию, ведь именно она явля­ется глав­ным ингре­ди­ен­том (после диа­го­на­ли­за­ции, конечно) в дока­за­тель­стве тео­ремы Клини о рекур­сии. (В сле­ду­ю­щих сооб­ще­ниях этот вопрос, воз­можно, будет иссле­до­ван более подробно.)

Заклю­че­ние

Целью этого сооб­ще­ния была демон­стра­ция воз­мож­но­сти авто­ма­ти­че­ского кон­стру­и­ро­ва­ния квай­нов на языке редак­тора sed с исполь­зо­ва­нием дока­за­тель­ства вто­рой рекур­сив­ной тео­ремы Клини в каче­стве основы. Резуль­тат про­ве­ден­ного экс­пе­ри­мента можно счи­тать поло­жи­тель­ным, хотя авто­ма­ти­че­ским такой спо­соб напи­са­ния квай­нов можно назвать только в смысле доста­точ­но­сти запуска един­ствен­ного скрипта generate.sh и лик­ви­да­ции наи­бо­лее слож­ной, и, если можно так выра­зиться, твор­че­ской состав­ля­ю­щей в кон­стру­и­ро­ва­нии само­реп­ли­ци­ру­ю­щейся системы.

В целом, сооб­ще­ние уже немного вышло за рамки очер­чен­ных целей. Но о квай­нах можно ска­зать боль­ше… В сле­ду­ю­щих сооб­ще­ниях этой серии.

Ссылки

М.И.Никитин
г.Алматы, июль, 2020
(послед­няя правка: ноябрь, 2020)


метки-категории: неподвижные-точки, sed, эзо­те­рика, про­грам­ми­ро­ва­ние, квай­но­вод­ство, теория-вычислимости, мате­ма­тика, фило­со­фия, само­ре­пли­ка­ция

[ЭЦП (SHA-256, RSA)] (ключ)