输出每个停止程序(编写并行解释器)


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     .     .
     ∞     .     .

目标语言要求

目标语言(一种并行解释的语言)必须是图灵完备的。除此之外,它可以是图灵完备的任何语言,包括大得多语言的图灵完备子集。您还可以自由解释诸如循环标签系统规则之类的内容。您还可以创建一种语言进行测试,只要您可以证明它为什么是图灵完备的即可。

举个例子,如果您选择测试Brainfuck,则最好仅测试[]-+<>子集,因为不支持输入,而只是丢弃输出(请参见下文)。

对于“控制器”程序(您正在打高尔夫球),没有特殊要求。普通语言限制。

如何创建程序的无限列表

大多数编程语言可以表示为有限字母中的一系列符号。在这种情况下,按长度增加的顺序枚举每个可能程序的列表相对容易。您使用的字母应代表目标语言的要求。在大多数情况下,这是可打印的ASCII。如果您的语言支持Unicode作为附加功能,则不应测试Unicode字符的所有可能组合,而只能测试ASCII。如果仅使用您的语言,[]-+<>则不要测试“注释” ASCII字符的各种组合。像APL这样的语言将有自己的特殊字母。

如果用Fractran或Turing Machines等非字母方式最好地描述您的语言,那么还有其他等效生成所有可能有效程序列表的等效方法。

解释不断增长的程序列表

挑战的关键部分是为越来越多的程序编写并行解释器。为此,有一些基本步骤:

  • 将有限数量的程序添加到列表中
  • 在有限的时间内分别解释列表中的每个程序。这可以通过对每个指令执行一个指令步骤来完成。保存所有状态。
  • 从列表中删除所有终止/抛出错误的程序
  • 输出干净暂停的程序
  • 在列表中添加更多程序
  • 依次模拟每个程序,从停下来的地方开始执行较旧的程序
  • 从列表中删除所有终止/抛出错误的程序
  • 输出干净暂停的程序
  • 重复

*您只应输出干净停止的程序。 这意味着在执行过程中不会引发语法错误或未捕获的异常。要求输入的程序也应终止而不输出它们。如果程序产生输出,则不应终止该输出,只需将输出丢弃即可。

更多规则

  • 您不得产生新线程来包含经过测试的程序,因为这会将并行化工作卸载到主机OS /其他软件上。
  • 编辑:为了弥补将来可能出现的漏洞,不允许您eval(或任何相关功能)一部分经过测试的程序的代码。您可以 eval从解释器的代码中获取代码块。(根据这些规则,BF-in-Python答案仍然有效。)
  • 这是
  • 你写你的提交中的语言并不需要是一样的,你要测试/输出的语言。
  • 您应该假定可用内存是无限的。
  • 证明图灵完备性时,可以假定输入已硬编码到程序中,并且可以从程序的内部状态读取输出。
  • 如果您的程序输出自身,则可能是错误的或多语言的。

7
我花了太长时间才意识到原因"If your program outputs itself, it is probably wrong or a polyglot."
trichoplax

1
我们可以假设可用内存是无限的(否则,我认为这是不可能的)
KSab 2015年

1
@KSab是的,否则绝对不可能。
PhiNotPi 2015年

1
后续挑战(难):输出每一个非停止程序。
米洛·布兰特

1
多次输出同一程序是否可以接受?

Answers:


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变体使用相对寻址,Wiki对话页建议使用此寻址以确保具有有界值的完整性。每次滴答时,将p[i+p[i+1]]-=p[i+p[i]]执行操作,然后i+=p[i+2]如果操作结果<= 0,则执行i+=3。如果我曾经是负数,程序将暂停。

此实现测试每个程序的初始状态由一个初始指令指针为0的一位非负整数(0-9)组成。

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

由于打高尔夫球的原因,输出被反转。上面的规范可以反过来重述,但是与实现中使用的代码不匹配,因此我没有这样描述。

编辑:显示简单无界增长的第一个程序是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是循环标签系统的极简变种。程序由两个二进制字符串定义:指令(循环)列表和初始状态。为了使我在打印程序时的生活更轻松,我定义了自己的符号:每个字符串都以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)。即使限于仅使用1作为初始数据字符串(“状态”),BCT也可以完成图灵完成的事实可以保存一些字节。例如,将二进制中的每个连续的正整数解释为1P,然后在1上执行P,然后输出P iff执行终止(再次进行燕尾加工)。(当然,任何P以0开始然后将名单上,因为这将立即删除初始datastring。)
水库

8

Python中的Brainfuck,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 2015年

@ Sp3000 The [-]and [+]是一个错误,现在应该修复,我已经用设置更新了
KSab 2015年

1
你为什么要支持.?对于BF的图灵完备子集来说是不必要的,并且无论如何应该忽略输出。另外,由于要包装单元格值,因此我认为您只需要-and之一+
Martin Ender 2015年

@MartinBüttner我似乎误解了这个问题;我没有读过“图灵完整子集”部分。但是,这是否使挑战几乎等同于(大多数)语言?您能否用Brainfuck(或什至更简单的东西)进行一对一替换,例如此处的c代码:en.wikipedia.org/wiki/Brainfuck#Commands
KSab 2015年

2
看一下stackoverflow.com/questions/1053931/…,尤其是OISC条目。另外,请查看CA Rule 110和循环标记系统。在这个挑战中,有很大的空间可以创造性地选择图灵完整的“语言”。
Sparr

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'以便允许使用python的bin(x)打高尔夫球。当解释器遇到\时,将输出下一个字符,并且两个字符都将被删除。遇到/时,它将查找搜索并替换为/ search / replace /的模式,包括模式中的转义字符(\\代表\,\ /代表/)。然后对该字符串重复执行该替换操作,直到不再存在搜索字符串为止,然后重新从头开始继续进行解释。程序为空时将暂停。如果未封闭的一组/ patterns或一个\之后没有字符,该程序将被杀死。

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

Java中的Treehugger1,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

Brachylog邮政对应问题,10个字节

≜;?{~c}ᵐ\d

在线尝试!

该函数是生成器,它会生成所有可能的Post对应问题,最终导致蛮力解决方案停止。(解决Post对应问题的强行解决方案称为图灵完成操作。)TIO链接包含一个标头,该标头将生成器转换为完整程序,并在生成每个输出时立即打印每个输出(因此,当TIO被杀死时)该程序由于消耗了60秒钟以上的执行时间,因此可以看到到目前为止产生的输出)。

这使用以下问题的表述方式:将字符串作为数字字符串给出,除0其本身之外不允许前导零,不接受涉及前导零的问题的解决方案,并且数字字符串可以表示为数字,或减去数字。显然,这都不会对语言的图灵完备性产生任何影响(因为完全不需要使用Post对应问题来使用数字零)。

该程序通过生成所有可能的问题解决方案来工作,然后向后工作以查找问题所解决的原始程序。这样,单个程序可以输出多次。目前尚不清楚这是否使答案无效。请注意,所有暂停程序最终将最终至少输出一次(实际上,无限次,因为具有解决方案的任何程序都具有无限多个解决方案),并且永远不会输出非暂停程序。

说明

≜;?{~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

锡兰,“没有I / O的紫色”,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从解释符号的意义,使得(潜在的)有效符号只是abABi1(最后一个只是为了读书,不是为了写)。

但是,由于Purple正在自我修改(并使用其源代码作为数据),可能包含其他字符的程序也很有用,因此我选择允许代码中所有可打印的(非空白)ASCII字符(其他字符可能是也很有用,但不那么容易打印)。

(您可以修改解释器,以取而代之以允许的字符串作为命令行参数–切换a下面定义的注释行。然后长度变为686字节。)

因此,我的“并行”解释器从这些字符创建了所有有限的字符串(以递增的长度和字典顺序)并尝试了每个有限的字符串。

每当从磁带上读取要执行的命令无效时,Purple都会无错误地停止运行-因此,没有无效的程序,并且有许多停止的程序。(即使在第一步中大多数都停止,只有一些长度为3的程序才能进入第二步(然后将停止),第一个非停止的程序的长度为6。

我认为按我的解释器尝试的顺序,第一个非停止程序是aaaiaa,它在第一步中将a寄存器设置为0(已经是),在第二步以及随后的每个步骤中,将指令指针设置为0,使它iaa再次执行。

我重用了为“标准” Purple解释器编写的部分代码,但是由于删除了输入和输出,因此并行解释器的长度甚至比该解释器更短,同时还包含了一次执行多个程序的附加逻辑。

这是带注释的格式版本:

// 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组合子演算Haskell中,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)

在线尝试!

怎么运行的

SK组合器演算的按值调用评估规则如下:

(一)s XYZXZYZ),对于Xÿž在正常形式;
(B)K XYX,对于Xÿ在正常形式;
(c)中的xyX ' ý,如果XX ';
(d)的xyXY ',对X在正常形式,如果ÿY'

由于我们仅对停止行为感兴趣,因此我们通过引入符号H来稍微扩展语言,该符号H不是正常形式,而是所有正常形式都对其“求值”:

(一)s XYZXZYZ),对于Xÿž在正常形式;
(B')K X ħ↦ X,对于X在正常形式;
(c)中的xyX ' ý,如果XX ';
(d)的xyXY ',对X在正常形式,如果ÿY' ;
(e)S↦H;
(f)K↦H;
(g)SH↦H;
(h)KH↦H;
(i)SHH↦H.

我们认为任何应用程序H x都是运行时错误,可以将其视为无限循环,并且我们对评估进行排序,使得(e)–(i)不会产生H,除非在可能的情况下忽略(顶层,任意k X ☐,任何忽略K☐,任何忽略小号X ☐对于X在正常形式,任何忽略S☐H)。这样,我们不会影响缺少H的常用术语的停止行为。

这些修改后的规则的好处在于,每个可归一化的术语都具有通往H的唯一评估路径,并且每个术语在under下均具有有限数量的可能的原像。因此,我们可以使用H来对所有反向评估路径进行更有效的广度优先搜索,而不是使用燕尾式方法。

n检查术语是否为正常形式,f找到该术语的所有可能的原像,以及l是否是通过H的广度优先搜索生成的可归一化术语的惰性无限列表。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.