Вивести кожну програму зупинки (написати паралельний перекладач)


26

Мета цього завдання - (врешті-решт) вивести всі можливі програми зупинки мовою на ваш вибір. Спочатку це може здатися неможливим, але ви можете це зробити дуже ретельним вибором порядку виконання.

Нижче наведена діаграма ASCII для ілюстрації цього. Нехай стовпці представляють нумерацію кожної можливої ​​програми (кожна програма - це кінцева кількість символів з кінцевого алфавіту). Нехай кожен рядок являє собою особливий крок у виконанні цієї програми. XПредставляють виконання в виконанні цієї програми в той час кроку.

 step#  p1 p2 p3 p4 p5 p6
     1  X  X  X  X  X  X
     2  X  X  X  X  X  
     3  X  X     X  X
     4  X  X     X  X
     5  X  X     X
     6     X     X
     7     X     X
     8     X     X
     9     X     X
     ∞     X     X

Як ви можете сказати, програми 2 і 4 не зупиняються. Якби ви виконували їх одночасно, ваш контролер застряг би у нескінченному циклі, який є програмою 2, і ніколи не виводить програми 3 і далі.

Натомість ви використовуєте підхід до доробки . Букви представляють можливий порядок виконання перших 26 кроків. В *s місця , де ця програма була зупинена і виводиться. В .s кроки , які не були виконані ще.

 step#  p1 p2 p3 p4 p5 p6
     1  A  C  F  J  N  R  V
     2  B  E  I  M  Q  *  Z
     3  D  H  *  P  U
     4  G  L     T  Y
     5  K  O     X
     6  *  S     .
     7     W     .
     8     .     .
     9     .     .
     ∞     .     .

Вимоги до цільової мови

Мова цілі (мова, що інтерпретується паралельно) повинна бути повною Тьюрінгом. Окрім цього, це може бути будь-яка мова, яка завершує Тьюрінг, включаючи підмножини Тюрінга з більш великими мовами. Ви також можете інтерпретувати речі, такі як правила циклічних тегів. Вам також дозволено створити мову для тестування, доки ви не зможете показати, чому він є повним Тьюрінгом.

Наприклад, якщо ви вирішили протестувати мозковий ебать, найкраще протестувати лише []-+<>підмножину, оскільки введення не підтримується, а вихід просто викидається (див. Нижче).

Якщо мова йде про програму "контролер" (яку ви займаєтеся гольфом), особливих вимог немає. Застосовуються звичайні мовні обмеження.

Як створити нескінченний список програм

Більшість мов програмування може бути представлена ​​у вигляді серії символів з кінцевого алфавіту. У цьому випадку перераховувати список кожної можливої ​​програми порівняно легко в порядку збільшення довжини. Алфавіт, який ви використовуєте, повинен відповідати вимогам цільової мови. У більшості випадків це друкується ASCII. Якщо ваша мова підтримує Unicode як додаткову функцію, не слід перевіряти кожну можливу комбінацію символів Unicode, лише ASCII. Якщо ваша мова використовується лише []-+<>тоді, не випробовуйте різні комбінації символів ASCII "коментувати". Такі мови, як APL, мали б свої власні алфавіти.

Якщо ваша мова найкраще описується якось не алфавітним способом, наприклад, Fractran або Turing Machines, то існують і інші не менш дійсні методи формування списку всіх можливих дійсних програм.

Інтерпретація постійно зростаючого списку програм

Ключова частина цього завдання полягає в тому, щоб написати паралельного перекладача для зростаючого списку програм. Для цього є кілька основних кроків:

  • Додайте до списку обмежену кількість програм
  • Інтерпретуйте кожну програму зі списку окремо протягом певного періоду часу. Це можна досягти, виконавши один крок інструкцій для кожного. Збережіть усі штати.
  • Видаліть зі списку всі програми, що завершують / викидають помилки
  • Виведіть чисто зупинені * програми
  • Додайте до списку ще кілька програм
  • Моделюйте кожну програму по черзі, підбираючи виконання старих програм там, де вона припинилася
  • Видаліть зі списку всі програми, що завершують / викидають помилки
  • Виведіть чисто зупинені * програми
  • повторити

* Ви повинні виводити лише ті програми, які чисто зупиняються. Це означає, що не було жодних синтаксичних помилок або невиконаних винятків, кинутих під час виконання. Програми, які вимагають введення, також повинні бути припинені без виведення їх. Якщо програма видає вихід, ви не повинні її припиняти, просто киньте вихід.

Більше правил

  • Ви не повинні породжувати нові теми, щоб містити тестовані програми, оскільки це вивантажує роботу паралелізації на хост ОС / інше програмне забезпечення.
  • Редагувати: щоб закрити майбутні лазівки, вам не дозволяється eval(або будь-яка пов’язана функція) частини коду перевіреної програми. Ви можете eval блокувати код з коду перекладача. (Відповідь BF-in-Python все ще діє у цих правилах.)
  • Це
  • Мова, в яку ви пишете свою заявку , не повинна відповідати мові, яку ви тестуєте / випускаєте.
  • Ви повинні припустити, що наявна пам’ять не обмежена.
  • Підтверджуючи повноту Тьюрінга, ви можете припустити, що введення жорстко кодується в програму, а вихід може бути прочитаний з внутрішнього стану програми.
  • Якщо ваша програма виводить себе, ймовірно, це неправильно або поліглот.

7
Мені знадобилося занадто багато часу, щоб зрозуміти, чому"If your program outputs itself, it is probably wrong or a polyglot."
трихоплакс

1
Можна припустити, що наявна пам'ять не обмежена (я не думаю, що це неможливо інакше)
KSab

1
@KSab Так, інакше це точно неможливо.
PhiNotPi

1
Подальший виклик ( набагато складніше): Виведіть кожну програму, що не зупиняється.
Міло Брандт

1
Чи прийнятно виводити одну і ту ж програму не один раз?

Відповіді:


9

subleq OISC в Python, 317 269 ​​байт

import collections
g=0
P={}
while 1:
    P[g]=[[0],collections.defaultdict(int,enumerate(list(int(x)for x in reversed(str(g)))))]
    g+=1
    for o,[a,p]in P.items():
        i=a[0]
        p[i+p[i+1]]-=p[i+p[i]]
        if p[i+p[i+1]]<=0:a[0]+=p[i+2]
        else:a[0]+=3
        if a[0]<0:print o;del P[o]

https://esolangs.org/wiki/Subleq

Програма subleq - це розширюваний список цілих чисел (p) та вказівник на інструкцію (i). Цей варіант subleq використовує відносну адресацію, яка, на думку вікі-сторінки для обговорення, потрібна для забезпечення повноти з обмеженими значеннями. Кожен галочок, операція p[i+p[i+1]]-=p[i+p[i]]виконується, і тоді, i+=p[i+2]якщо результат операції був <= 0, інакше i+=3. Якщо я колись негативний, програма зупиняється.

Ця реалізація перевіряє кожну програму, початковий стан якої складається з одноцифрових невід'ємних цілих чисел (0-9) з початковим вказівником інструкції 0.

Output:
21 (which represents the program [1 2 0 0 0 0 0...]
121
161
221
271
351
352
461
462
571
572
681
682
791
792

Вихід зворотний, з міркувань гольфу. Викладені вище характеристики можуть бути перезапущені в зворотному порядку, але тоді вони не збігаються з кодом, який використовується в реалізації, тому я не описав це таким чином.

EDIT: Перша програма, що демонструє просте необмежене зростання, - 14283, яка зменшує значення в місці пам'яті 6 і записує явний 0 (на відміну від неявного 0 у кожній комірці) до наступної негативної комірки, кожні три тики.


9

Побітовий циклічний тег у CJam, 98 87 84 77 байт

L{Z):Z2b1>_,,1>\f{/([\:~]a2*}+{)~[\({1+(:X+\_0=Xa*+}{0+\1>}?_{]a+}{];p}?}%1}g

Оскільки це нескінченний цикл, ви не можете безпосередньо перевірити це в онлайн-перекладачі. Однак, ось альтернативна версія, яка читає кількість ітерацій STDIN, з якими ви можете пограти. Для тестування повної програми вам знадобиться перекладач Java .

BCT - мінімалістичний варіант Cyclic Tag Systems . Програма визначається двома двійковими рядками: циклічним списком інструкцій та початковим станом. Щоб полегшити моє життя під час друку програм, я визначив власну нотацію: кожна з рядків задана як масив цілих чисел у стилі CJam, а вся програма оточена [[...]], наприклад

[[[0 0 1 1] [0 1 1 1 0]]]

Я також забороняю порожні початкові стани або порожні списки інструкцій.

Інструкції в BCT інтерпретуються так:

  • Якщо інструкція є 0, вийміть провідний біт із поточного стану.
  • Якщо інструкція є 1, прочитайте ще один біт зі списку інструкцій, зателефонуйте до цього X. Якщо біт від поточного стану є 1, додайте Xдо поточного стану, інакше нічого не робити.

Якщо поточний стан коли-небудь стає порожнім, програма зупиняється.

Перші кілька програм зупинки

[[[0] [0]]]
[[[0] [1]]]
[[[0 0] [0]]]
[[[0] [0 0]]]
[[[0 0] [1]]]
[[[0] [0 1]]]
[[[0 1] [0]]]
[[[0] [1 0]]]
[[[0 1] [1]]]
[[[0] [1 1]]]

Якщо ви хочете побачити більше, перегляньте версію в онлайн-перекладачі, який я пов’язував вище.

Пояснення

Ось як працює код. Для того, щоб відслідковувати доветалінг, у нас завжди буде масив на стеку, який містить усі програми. Кожна програма є парою внутрішнього представлення програмного коду (як [[0 1 0] [1 0]]), а також поточного стану програми. Ми будемо використовувати останнє лише для обчислення, але нам потрібно буде пам’ятати перше, щоб надрукувати програму, як тільки вона зупиниться. Цей список програм просто ініціалізований у порожній масив L.

Решта коду - це нескінченний цикл, {...1}gякий спочатку додає одну або кілька програм до цього списку і обчислює один крок для кожної програми. Програми, які зупиняються, друкуються та видаляються зі списку.

Я перераховую програми, підраховуючи двійкове число. Провідна цифра знімається, щоб гарантувати, що ми зможемо отримати всі програми також із провідними 0. Для кожного такого усіченого бінарного подання я натискаю по одній програмі для кожного можливого поділу між інструкціями та початковим станом. Наприклад, якщо лічильник зараз знаходиться 42, його бінарне представлення є 101010. Ми позбавляємося від провідних 1і витісняємо всі непусті розщеплення:

[[[0] [1 0 1 0]]]
[[[0 1] [0 1 0]]]
[[[0 1 0] [1 0]]]
[[[0 1 0 1] [0]]]

Оскільки ми не хочемо порожніх інструкцій чи штатів, ми запускаємо лічильник на 4, який дає [[[0] [0]]]. Цей перелік проводиться за допомогою наступного коду:

Z):Z    e# Push Z (initially 3), increment, and store in Z.
2b1>    e# Convert to base 2, remove initial digit.
_,      e# Duplicate and get the number of bits N.
,1>     e# Turn into a range [1 .. N-1].
\       e# Swap the range and the bit list.
f{      e# Map this block onto the range, copying in the bit list on each iteration.
  /     e#   Split the bit list by the current value of the range.
  (     e#   Slice off the first segment from the split.
  [     
    \:~ e#   Swap with the other segments and flatten those.
  ]     e#   Collect both parts in an array.
  a2*   e#   Make an array that contains the program twice, as the initial state is the
        e#   same as the program itself.
}
+       e# Add all of these new programs to our list on the stack.

Решта коду відображає блок на список програм, який виконує один крок обчислення BCT на другій половині цих пар і видаляє програму, якщо вона зупиняється:

)~     e# Remove the second half of the pair and unwrap it.
[      e# We need this to wrap the instructions and current state back in an array
       e# again later.
\(     e# Bring the instruction list to the top and remove the leading bit.
{      e# If it's a 1...
  1+   e#   Append a 1 again (the instructions are cyclic).
  (:X+ e#   Remove the next bit, store it in X and also append it again.
  \_0= e#   Bring the current state to the top, get its first bit.
  Xa*+ e#   Append X if that bit was 1 or nothing otherwise.
}{     e# Else (if it's a 0...)
  0+   e#   Append a 0 again (the instructions are cyclic).
  \1>  e#   Discard the leading bit from the current state.
}?
_      e# Duplicate the current state.
{      e# If it's non-empty...
  ]a+  e#   Wrap instructions and state in an array and add them to the program
       e#   pair again.
}{     e# Else (if it's empty)...
  ];p  e# Discard the instructions and the current state and print the program.
}?

Ніцца (+1). Деякі байти можуть бути збережені, використовуючи той факт, що BCT закінчується Тьюрінгом, навіть якщо він обмежений використанням лише 1 в якості початкового рядка даних (ваш "стан"). Наприклад, інтерпретуйте кожне наступне додатне ціле число у двійковій формі як 1P, потім виконайте P на 1 і виведіть P iff виконання завершується (повторне підключення). (Звичайно, будь-яка P , який починається з 0 потім буде в списку, так що негайно видалити початковий datastring.)
рес

8

Brainfuck в Python, 567 байт

Порівняно просте рішення, оскільки Brainfuck навряд чи є найважчою мовою для написання перекладача.

Ця реалізація Brainfuck має покажчик даних, починаючи з 0, дозволено приймати позитивне значення (вважається помилкою, якщо вона намагається пройти зліва від 0). Осередки даних можуть приймати значення від 0 до 255 і обертати. 5 дійсних інструкцій є ><+[]( -непотрібними через обгортання).

Я думаю, що зараз все правильне, проте важко бути впевненим, що він друкує всі можливі рішення, тому я, можливо, не вистачаю деяких.

o="><+]["
A="[]if b%s1<0else[(p,a+1,b%s1,t+[0],h)]"
C="[(p,h[a]+1,b,t,h)if t[b]%s0else(p,a+1,b,t,h)]"
I=lambda s,i:i*">"if""==s else o[o.find(s[0])+i-5]+I(s[1:],i*o.find(s[0])>3)
s="";l=[]
while 1:
 s=I(s,1)
 r=[];h={}
 for i in range(len(s)):
    if s[i]=="[":r+=[i]
    if s[i]=="]":
     if r==[]:break
     h[r[-1]]=i;h[i]=r[-1];r=r[:-1]
 else:
    if r==[]:
     l+=[(s,0,0,[0],h)];i=0
     while i<len(l):
        p,a,b,t,h=l[i]
        if a>=len(p):print p;l[i:i+1]=[]
        else:l[i:i+1]=eval([A%("+","+"),A%("-","-"),"[(p,a+1,b,t[:b]+[(t[b]+1)%256]+t[b+1:],h)]",C%">",C%"=="][o.find(p[a])]);i+=1

Перші кілька результатів:

>
+
>>
+>
><
>+
++
[]
>>>
+>>

І список першого 2000 року: http://pastebin.com/KQG8PVJn

І, нарешті, список перших 2000 виходів, що містяться []в них: http://pastebin.com/iHWwJprs
(усі інші тривіальні, доки вони дійсні)

Зауважте, що висновок не є впорядкованому порядку, хоча для багатьох з них може виглядати таким чином, оскільки програми, що займають більше часу, будуть надруковані пізніше.


1
І голі, [-]і [+]обов'язково повинні з'являтися, оскільки вміст циклу просто пропускається (без обгортання).
PhiNotPi

@ Sp3000 [-]і [+]була помилка , яка повинна тепер фіксованою і я оновив з настройками
KSab

1
Чому ви підтримуєте .? Це не обов'язково для повної підмножини BF, що закінчується Тьюрінгом, і результат все одно слід ігнорувати. Крім того, оскільки ви обертаєте значення комірок навколо, я думаю, вам потрібен лише один з -і +.
Мартін Ендер

@ MartinBüttner Я, здається, неправильно зрозумів питання; Я не читав частину "повного підмножини". Однак це не робить виклик майже еквівалентним для більшості мов? Не могли ви просто зробити заміну 1 на 1 Brainfuck (або, можливо, щось навіть простіше), наприклад код c тут: en.wikipedia.org/wiki/Brainfuck#Commands .
KSab

2
Погляньте на stackoverflow.com/questions/1053931/…, зокрема, запис OISC. Крім того, перегляньте Правила 110 CA та Циклічні системи міток. У цьому виклику є багато місця для творчого вибору затятої повноцінної "мови".
Спарр

5

косої риси в Python, 640 498 байт

g=2
P={}
while 1:
    b=bin(g)[3:]
    P[b]=[[0],['',''],[b]]
    g+=1
    for d,[a,b,c]in P.items():
        s=c[0]
        if a[0]:
            if s.count(b[0]):s=s.replace(b[0],b[1],1)
            else:a[0]=0
        else:
            if s[0]=='0':
                if len(s)==1:del P[d];continue
                s=s[2:]
            else:
                b[0]=b[1]=''
                a[0]=1
                t=p=0
                while t<2:
                    p+=1
                    if p>=len(s):break
                    if s[p]=='0':
                        if p+1>=len(s):break
                        b[t]+=s[p+1]
                        p+=1
                    else:t+=1
                if t<2:del P[d];continue
        c[0]=s
        if len(s)==0:print d;del P[d]

https://esolangs.org/wiki////

Програма косої риски - це рядок, в цьому інтерпретаторі обмежено символами '/' та '\'. У цій реалізації, / є '1' і \ є '0', щоб дозволити проводити деякий гольф із використанням бункера пітона (x). Коли інтерпретатор стикається з \, виводиться наступний символ і обидва символи видаляються. Коли він стикається з a /, він шукає шаблони пошуку та заміни як / search / substitute / включаючи втечені символи у межах шаблонів (\\ являє \ і \ / представляє /). Потім операція заміни виконується на рядку повторно, поки рядок пошуку більше не присутній, потім інтерпретація продовжується з початку знову. Програма зупиняється, коли вона порожня. Програма буде знищена, якщо після неї буде незакритий набір / шаблонів або \, без символу.

Example output and explanations:
01 outputs '1' and halts
00 outputs '0' and halts
0101 outputs '11' and halts
0100 ...
0001
0000
010101
010100
010001
010000 ...
101110 replaces '1' with '', leaving '00', which outputs '0' and halts

4

Treehugger у Java, 1,299 1,257 1,251 1,207 1,203 1,201 1,193 1,189 байт

import java.util.*;class I{static class N{N l,r;byte v;}static class T extends Stack<N>{{push(new N());}void p(){pop();if(size()==0)p();}int i,h;char[]s;}static void s(T t){if(t.i>=t.s.length){t.h=1;return ;}char c=t.s[t.i];if(c=='<'){if(t.peek().l==null)t.peek().l=new N();t.push(t.peek().l);}if(c=='>'){if(t.peek().r==null)t.peek().r=new N();t.push(t.peek().r);}if(c=='^')t.p();if(c=='+')t.peek().v++;if(c=='-')t.peek().v--;if(c=='['&&t.peek().v==0){int i=1;while(i>0){t.i++;if(t.s[t.i]==']')i--;if(t.s[t.i]=='[')i++;}return;}if(c==']'&&t.peek().v!=0){int i=1;while(i>0){t.i--;if(t.s[t.i]==']')i++;if(t.s[t.i]=='[')i--;}return;}t.i++;}static char[]n(char[]a){String b="<^>[+-]";int q=a.length;for(int i=q-1;i>=0;i--){int j=b.indexOf(a[i]);if(j<6){a[i]=b.charAt(j+1);return a;}a[i]='<';}a=Arrays.copyOf(a,q+1);a[q]='<';return a;}public static void main(String[]a){List<T>z=new ArrayList<T>();char[]c={};while(true){T t=new T();t.s=c;if(b(c))z.add(t);c=n(c.clone());for(T u:z)try{s(u);if(u.h>0){z.remove(u);System.out.println(u.s);break;}}catch(Exception e){z.remove(u);break ;}}}static boolean b(char[]c){int i=0;for(char d:c){if(d=='[')i++;if(d==']')i--;if(i<0)return 0>0;}return i==0;}}

4

БрахілогПроблема кореспонденції після публікації , 10 байт

≜;?{~c}ᵐ\d

Спробуйте в Інтернеті!

Функція, яка є генератором, що генерує всі можливі проблеми кореспонденції після публікації, для яких рішення, що примушують жорстоко, врешті-решт зупиняються. (Рішення з примусовою обробкою проблеми кореспонденції по пошті, як відомо, є операцією Тьюрінга. програма через те, що витрачається більше 60 секунд часу виконання, видно зроблений до цього часу вихід).

При цьому використовується постановка задачі, в якій рядки задаються у вигляді рядків цифр, провідні нулі не дозволяються, за винятком 0самих себе, рішення проблеми, що включає провідні нулі, не приймаються, а рядок цифр може бути представлена ​​як числом або мінус кількість. Зрозуміло, що нічого з цього не впливає на цілісність мови Тьюрінга (оскільки немає необхідності в проблемі листування в Пості, щоб взагалі використовувати цифру нуля).

Ця програма працює за допомогою генерування всіх можливих рішень проблем, а потім працює назад, щоб знайти оригінальні програми, які вирішуються ними. Як така, окрему програму можна виводити багато разів. Незрозуміло, чи це визнає недійсною відповідь чи ні; зауважте, що всі програми зупинки в кінцевому рахунку будуть виведені принаймні один раз (насправді, нескінченно багато разів, оскільки будь-яка програма, яка має рішення, має нескінченно багато рішень), а програми, що зупиняють роботу, ніколи не виводяться.

Пояснення

≜;?{~c}ᵐ\d
≜           Brute-force all numbers:
 ;?           Pair {the number} with {itself}
   {  }ᵐ      For each pair element:
    ~c          Brute-force a partition of that element into substrings
        \     such that the two elements each have the same number of substrings
        \     and group together corresponding substrings
         d    and remove duplicated pairs {to produce a possible output}

2

"Фіолетовий без вводу / виводу" в Цейлоні, 662

import ceylon.language{l=variable,I=Integer,m=map,S=String}class M(S d){l value t=m{*d*.hash.indexed};l I a=0;l I b=0;l I i=0;I g(I j)=>t[j]else 0;value f=m{97->{a},98->{b},65->{g(a)},66->{g(b)},105->{i},49->{1}};value s=m{97->((I v)=>a=v),98->((I v)=>b=v),65->((I v)=>t=m{a->v,*t}),66->((I v)=>t=m{b->v,*t}),105->((I v)=>i=v)};I&I(I)x{throw Exception(d);}I h(I v)=>f[v]?.first else x;shared void p(){(s[g(i)]else x)(h(g(i+1))-h(g(i+2)));i+=3;}}shared void run(){value a='!'..'~';{S*}s=expand(loop<{S*}>{""}((g)=>{for(c in a)for(p in g)p+"``c``"}));l{M*}r={};for(p in s){r=[M(p),*r];for(e in r){try{e.p();}catch(x){print(x.message);r=r.filter(not(e.equals));}}}}

Фіолетовий - це мова, що змінюється на одній інструкції, яку змінюють тут . Як введення і виведення не актуальні для вирішення цього завдання, я витягував oзміст символу з інтерпретатора, таким чином, щоб (потенційно) дійсні символи просто a, b, A, B, iі 1(остання тільки для читання, а не для запису).

Але оскільки Purple самомодифікується (і використовує свій вихідний код у якості даних), потенційно також можуть бути корисні програми, що містять інші символи, крім цих символів, тому я вирішив дозволити всі друковані (непробілимі) ASCII символи в коді (інші можуть бути також корисні, але не так легко друкуються).

(Ви можете змінити інтерпретатора, щоб замість нього взяти рядок дозволених символів як аргумент командного рядка - переключити коментований рядок, що визначається aнижче. Тоді довжина стає 686 байт.)

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

Фіолетовий буде зупинятися без помилок, коли команда, яку слід зчитувати зі стрічки для виконання, не є дійсною - таким чином, недійсних програм немає, і багато, багато зупиняючих програм. (Більшість зупиняється навіть на першому кроці; лише деякі програми довжиною 3 підходять до другого кроку (і зупиняються тоді), перші неприпинюючі мають довжину 6.

Я думаю, що найпершою програмою, що не зупиняється, у порядку, спробуваному моїм інтерпретатором, є те aaaiaa, що на першому кроці встановлює aрегістр на 0 (який він уже був), а на другому та кожному наступному кроці вказівник інструкцій повертає до 0, змушує його iaaзнову виконати .

Я повторно використовував частину коду, написаного для мого інтерпретатора "стандартного" фіолетового , але завдяки вилученню вводу та виводу мій паралельний перекладач стає ще трохи коротшим за це, включаючи додаткову логіку для виконання декількох програм одночасно.

Ось коментована та відформатована версія:

// Find (enumerate) all halting programs in (a non-I/O subset of) Purple.
//
// Question:  /codegolf//q/51273/2338
// My answer: /codegolf//a/65820/2338

// We use a turing-complete subset of the Purple language,
// with input and output (i.e. the `o` command) removed.

import ceylon.language {
    l=variable,
    I=Integer,
    m=map,
    S=String
}

// an interpreting machine.
class M(S d) {
    // The memory tape, as a Map<Integer, Integer>.
    // We can't modify the map itself, but we
    // can replace it by a new map when update is needed.
    l value t = m {
        // It is initialized with the code converted to Integers.
        // We use `.hash` instead of `.integer` because it is shorter.
        *d*.hash.indexed
    };

    // three registers
    l I a = 0;
    l I b = 0;
    l I i = 0;

    // get value from memory
    I g(I j) =>
            t[j] else 0;

    // Map of "functions" for fetching values.
    // We wrap the values in iterable constructors for lazy evaluation
    //  – this is shorter than using (() => ...).
    // The keys are the (Unicode/ASCII) code points of the mapped
    // source code characters.
    value f = m {
        // a
        97 -> { a },
        // b
        98 -> { b },
        // A
        65 -> { g(a) },
        // B
        66 -> { g(b) },
        // i
        105 -> { i },
        // 1
        49 -> { 1 }
    };

    // Map of functions for "storing" results.
    // The values are void functions taking an Integer,
    // the keys are the ASCII/Unicode code points of the corresponding
    // source code characters.
    value s = m {
        // a
        97 -> ((I v) => a = v),
        // b
        98 -> ((I v) => b = v),
        // Modification of the memory works by replacing the map with
        // a new one.
        // This is certainly not runtime-efficient, but shorter than
        // importing ceylon.collections.HashMap.
        // A
        65 -> ((I v) => t = m { a->v, *t }),
        // B
        66 -> ((I v) => t = m { b->v, *t }),
        // i
        105 -> ((I v) => i = v)
    };


    // Exit the interpretation, throwing an exception with the machine's
    // source code as the message.  The return type is effectively `Nothing`,
    // but shorter (and fits the usages).
    I&I(I) x {
        throw Exception(d);
    }

    // accessor function for the f map
    I h(I v) =>
            f[v]?.first else x;

    // a single step
    shared void p() {
        (s[g(i)] else x)(h(g(i + 1)) - h(g(i + 2)));
        i += 3;
    }
}

// the main entry point
shared void run() {
    // the alphabet of "Purple without I/O".
    value a = '!'..'~';
    //// possible alternative to use a command line argument:
    // value a = process.arguments[0] else '!'..'~';

    // an iterable consisting of all programs in length + lexicographic order
    {S*} s =
            // `expand` creates a single iterable (of strings, in this case)
            // from an iterable of iterables (of strings).
             expand(
        // `loop` creates an iterable by applying the given function
        // on the previous item, repeatedly.
        // So here we start with the iterable of length-zero strings,
        // and in each iteration create an iterable of length `n+1` strings
        // by concatenating the length `n` strings with the alphabet members.
        loop<{S*}>{ "" }((g) =>
                {
                    for (c in a)
                        for (p in g)
                            p + "``c``"
                }));

    // This is a (variable) iterable of currently running machines.
    // Initially empty.
    l {M*} r = {};

    // iterate over all programs ...
    for(p in s) {
        // Create a machine with program `p`, include it
        //  in the list of running machines.
        //
        // We use a sequence constructor here instead of
        //  an iterable one (i.e. `r = {M(p, *r)}` to prevent
        // a stack overflow when accessing the deeply nested
        // lazy iterable.
        r = [M(p), *r];
        // iterate over all running machines ...
        for(e in r) {
            try {
                // run a step in machine e.
                e.p();
            } catch(x) {
                // exception means the machine halted.
                // print the program
                print(x.message);
                // remove the machine from the list for further execution
                r = r.filter(not(e.equals));
            }
        }
        // print(r.last);
    }
}

2

Обчислення комбінатора SK в Хаскеллі , 249 байт

data C=H|S|K|C:$C deriving(Eq,Show)
n(a:$b)=m a*n b
n a=m a
m S=1
m K=1
m(S:$a)=n a
m _=0
f H=[S,K,S:$H,K:$H,S:$H:$H]
f a=[S:$b:$c:$d|b:$d:$(c:$e)<-[a],d==e,n b*n c*n d>0]++[K:$a:$H|n a>0]++do b:$c<-[a];[d:$c|d<-f b]++[b:$d|n b>0,d<-f c]
l=H:(f=<<l)

Спробуйте в Інтернеті!

Як це працює

Правила оцінки виклику за вартістю для обчислення комбінатора СК є наступними:

(a) S xyzxz ( yz ), для x , y , z у звичайній формі;
(b) K xyx , для x , y у звичайній формі;
(c) xyxy , якщо xx ′;
(d) xyxy ′, для x у звичайній формі, якщо yy ′ .

Оскільки нас цікавить лише зупинка поведінки, ми трохи розширюємо мову, вводячи символ H, який не є в нормальній формі, але який "нормальні форми" оцінюють:

(a) S xyzxz ( yz ), для x , y , z у звичайній формі;
(b ') K x H ↦ x , для x у звичайній формі;
(c) xyxy , якщо xx ′;
(d) xyxy ′, для x у звичайній формі, якщо yy ′ ;
(e) S ↦ H;
(f) K ↦ H;
(g) SH ↦ H;
(h) KH ↦ H;
(i) SHH ↦ H.

Ми вважаємо, що будь-яка програма H x є помилкою під час виконання, яку слід трактувати як би нескінченну цикл, і ми замовляємо оцінки таким чином, що жодна H не виробляється за допомогою (e) - (i), за винятком контексту, де це буде ігнорується (верхній рівень, будь-який K x ☐, будь-який ігнорований K☐, будь-який ігнорований S x ☐ для x у звичайній формі, будь-який ігнорований S☐H). Таким чином ми не впливаємо на зупинку поведінки звичайних термінів, у яких бракує H.

Переваги цих модифікованих правил полягають у тому, що кожен нормалізований термін має унікальний шлях оцінювання до Н і що кожен термін має обмежену кількість можливих примірників за under. Тож замість того, щоб використовувати підхід до доопрацювання, ми можемо зробити набагато ефективніший пошук на ширині для всіх шляхів зворотної оцінки від H.

nперевіряє, чи є термін у нормальній формі, fзнаходить усі можливі переваги терміна, і lє ледачим нескінченним переліком нормованих термінів, що утворюються шляхом широкого пошуку від H.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.