すべての停止プログラムを出力します(並列インタープリターを作成します)


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は停止しません。それらを一度に1つずつ実行すると、コントローラーはプログラム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のように、言語がアルファベット以外の方法で最もよく記述されている場合、有効なすべての有効なプログラムのリストを生成する他の同等の有効な方法があります。

増え続けるプログラムのリストの解釈

この課題の重要な部分は、増え続けるプログラムのリスト用の並列インタープリターを作成することです。これにはいくつかの基本的な手順があります。

  • 有限数のプログラムをリストに追加します
  • リスト上の各プログラムを一定期間、個別に解釈します。これは、それぞれに対して1つの命令ステップを実行することで実現できます。すべての状態を保存します。
  • リストからすべての終了/エラースロープログラムを削除します
  • 完全に停止した*プログラムを出力する
  • さらにプログラムをリストに追加します
  • 各プログラムを順番にシミュレートし、中断したところから古いプログラムの実行を再開します
  • リストからすべての終了/エラースロープログラムを削除します
  • 完全に停止した*プログラムを出力する
  • 繰り返す

*正常に停止するプログラムのみを出力する必要があります。 これは、実行中にスローされる構文エラーやキャッチされない例外がなかったことを意味します。入力を要求するプログラムも、出力せずに終了する必要があります。プログラムが出力を生成する場合、それを終了するのではなく、出力を破棄するだけです。

その他のルール

  • テストしたプログラムを含めるために新しいスレッドを生成しないでください。これにより、並列化の作業がホストOS /他のソフトウェアにオフロードされます。
  • 編集:潜在的な将来の抜け穴を閉じるためにevalテストされたプログラムのコードの一部(または関連する機能)を許可されていません。あなたはでき evalインタプリタのコードからコードブロックを。(BF-in-Pythonの答えは、これらのルールの下でも有効です。)
  • これは
  • 提出物を書く言語は、テスト/出力している言語と同じである必要ありません
  • 使用可能なメモリには制限がないと想定する必要があります。
  • チューリング完全性を証明する場合、入力はプログラムにハードコーディングされ、プログラムの内部状態から出力を読み取ることができると想定できます。
  • プログラムがそれ自体を出力する場合、それはおそらく間違っているか、多言語です。

7
理由を理解するのに時間がかかりすぎた"If your program outputs itself, it is probably wrong or a polyglot."
-trichoplax

1
利用可能なメモリが無制限であると仮定してもよいです(そうでなければこれは可能だとは思いません)
-KSab

1
@KSabはい、そうでなければ間違いなく不可能です。
PhiNotPi

1
フォローアップの課題(はるかに難しい):すべての停止しないプログラムを出力します。
ミロブラント

1
同じプログラムを複数回出力することは許容されますか?

回答:


9

Pythonのsubleq OISC、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。iが負の場合、プログラムは停止します。

この実装は、初期状態が1桁の非負整数(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

ゴルフの理由で、出力は逆になります。上記の仕様は逆に言い換えることができますが、実装で使用されるコードと一致しないため、その方法については説明していません。

編集:単純な無制限の成長を示す最初のプログラムは14283で、メモリ位置6の値をデクリメントし、3ティックごとに次の負のセルに明示的な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のミニマリスト版です。プログラムは、2つのバイナリ文字列で定義されます:命令の(循環)リストと初期状態。プログラムを印刷するときに私の生活を楽にするために、私は独自の表記法を定義しました。各文字列は整数の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で、最初にこのリストに1つ以上のプログラムを追加し、各プログラムで1ステップを計算します。停止するプログラムは印刷され、リストから削除されます。

2進数をカウントアップしてプログラムを列挙しています。先頭の0が付いたすべてのプログラムを取得できるように、先頭の数字は取り除かれます。このような切り捨てられたバイナリ表現ごとに、命令と初期状態の間の可能な分割ごとに1つのプログラムをプッシュします。たとえば、カウンタが現在ある場合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計算の1ステップを実行し、停止した場合はプログラムを削除します。

)~     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を出力します。(もちろん、0で始まるすべてのPは、最初のデータ文字列をすぐに削除するため、リストに表示されます。)
res

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

SP3000ザ・@ [-][+]なりまし固定すべきであると私は設定で更新しましたバグだった
KSab

1
なぜあなたはサポートしてい.ますか?BFのチューリング完全なサブセットには必要ありません。出力はとにかく無視する必要があります。また、セル値をラップしているので、必要なのは-とのいずれかだけだと思います+
マーティンエンダー

@MartinBüttner質問を誤解したようです。「チューリング完全サブセット」の部分は読みませんでした。ただし、これにより、(ほとんどの)言語で課題がほぼ同等になりませんか?Brainfuck(またはもっと単純なもの)で1対1の置き換えを行うことはできませんでした。たとえば、ここのcコードはen.wikipedia.org/wiki/Brainfuck#Commandsです。
KSab

2
見てみましょうstackoverflow.com/questions/1053931/...特にOISCエントリを。また、CA Rule 110およびCyclic Tag Systemsを調べてください。この課題では、チューリング完全な「言語」を創造的に選択する余地がたくさんあります。
スパー

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

オンラインでお試しください!

ブルートフォーシングソリューションが最終的に停止する可能性のあるすべてのポスト通信問題を生成するジェネレーターである機能。(ポスト通信の問題に対する強引な解決策はチューリング完全な操作であることが知られています。)TIOリンクには、ジェネレーターを完全なプログラムに変換するヘッダーが含まれており、生成されるとすぐに各出力を印刷します(したがって、TIOが強制終了するとプログラムの実行時間が60秒を超えるため、これまでに生成された出力が表示されます)。

これは、文字列が数字の文字列として与えられる問題の定式化を使用し、0それ自体を除いて先頭のゼロは許可されず、先頭のゼロを含む問題の解決策は受け入れられず、数字の文字列は数字として表すことができます、またはマイナス数。明らかに、これは言語のチューリング完全性に影響を与えません(Post通信の問題で数字のゼロをまったく使用する必要がないため)。

このプログラムは、問題に対するすべての可能な解決策を生成し、それらによって解決される元のプログラムを見つけるために逆方向に作業することで機能します。そのため、個々のプログラムを何度も出力できます。これが回答を無効にするかどうかは不明です。停止しているすべてのプログラムは最終的に少なくとも1回出力されることに注意してください(実際、ソリューションを持つプログラムには無限に多くのソリューションがあるため、無限に何度も出力されます)。

説明

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

パープルは、ここで解釈するように求められた自己修正型の1命令言語です。入力と出力はこのタスクに関連しないためo、(潜在的に)有効なシンボルがちょうどaになるように、インタープリターからシンボルの意味を削除しました。bABiおよび1(書き込みのためだけに読み取るための最後の1、ではありません)。

しかし、パープルは自己変更するため(ソースコードをデータとして使用するため)、それらの文字以外の文字を含むプログラムも有用である可能性があるため、コード内のすべての印刷可能な(空白以外の)ASCII文字を許可することを選択しました(他の文字は同様に便利ですが、印刷は簡単ではありません)。

(代わりに、許可された文字列をコマンドライン引数として使用するようにインタープリターを変更できaます。以下で定義するコメント行を切り替えます。その後、長さは686バイトになります。)

したがって、私の「並列」インタプリタは、それらの文字からすべての有限文字列を作成し(長さおよび辞書順で)、それぞれを試行します。

紫色は、実行のためにテープから読み取られるコマンドが有効でない場合はエラーなしで停止します。したがって、無効なプログラムはなく、多くの停止プログラムが多数あります。(ほとんどの場合、最初のステップでも停止します。長さ3のプログラムの一部のみが2番目のステップに到達し(その後停止します)、最初の停止しないプログラムの長さは6です。

私のインタープリターが試した最初の非停止プログラムaaaiaaは、最初のステップでaレジスタを0に(すでにあった)、2番目以降のステップで命令ポインタを0に戻すiaa再度実行させます。

「標準」パープルのインタープリター書かれたコードの一部を再利用しましたしましたが、入出力の削除により、並列インタープリターはそれよりもわずかに短くなり、同時に複数のプログラムを一度に実行するための追加ロジックを含みます。

これはコメント付きでフォーマットされたバージョンです。

// Find (enumerate) all halting programs in (a non-I/O subset of) Purple.
//
// Question:  https://codegolf.stackexchange.com/q/51273/2338
// My answer: https://codegolf.stackexchange.com/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)

オンラインでお試しください!

使い方

SKコンビネーター計算の値ごとの評価ルールは次のとおりです。

()S XYZXZYZ)のためのXYZの正規形で;
(B)K XYXについてのxyの通常の形で;
(c)はXYX ' 、Y、もしXX '。
(D)XYXY 'の、X通常の形で、もしYY'

停止動作にのみ関心があるため、通常の形式ではなく、すべての通常の形式が「評価」する記号Hを導入することにより、言語をわずかに拡張します。

()S XYZXZYZ)のためのXYZの正規形で;
(B ')Kは、xは H↦ Xのため、X通常の形で;
(c)はXYX ' 、Y、もしXX '。
(D)XYXY 'の、X通常の形で、もしYY'
(e)S↦H;
(f)K↦H;
(g)SH↦H;
(h)KH↦H;
(i)SHH↦H.

アプリケーションH xは実行時エラーであると見なし、無限ループであるかのように処理し、(e)–(i)によってHが生成されないように評価します。無視(トップレベル、任意のK X ☐、任意無視K☐、任意無視S X ☐用X正規形で、いずれかのS☐Hを無視)。このようにして、Hを欠く通常の用語の停止動作に影響を与えません。

これらの変更されたルールの利点は、すべての正規化可能な用語がHへの一意の評価パスを持ち、すべての用語がunderの下で可能な数のプリイメージを持っていることです。そのため、ダブテールアプローチを使用する代わりに、Hからのすべての逆評価パスのはるかに効率的な幅優先検索を実行できます。

n用語が正規形であるかどうかをチェックし、用語のfすべての可能な前画像を見つけl、Hからの幅優先検索によって生成された正規化可能な用語の怠zyな無限リストです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.