編集:あなたの一部が疑ったように、公式インタープリターにバグがありました:の構成の順序.
が逆になりました。私は2つのバージョンのインタープリターを持っていて、ここでは間違ったバージョンを使用しました。例は、この誤ったバージョン用にも書かれています。リポジトリのインタープリターと以下の例を修正しました。の説明>
も少しあいまいだったので修正しました。また、時間がかかることをお詫びし、実生活に巻き込まれました。
EDIT2:私のインタプリタの実装にはバグがあり.
、例に反映されていました(未定義の動作に依存していました)。この問題は修正されました。
前書き
Shiftは、数年前に作成した難解な関数型プログラミング言語ですが、今日公開されました。スタックベースですが、Haskellのような自動カレーも備えています。
仕様
Shiftには2つのデータ型があります。
- 任意の正のアリティ(入力数)を持ち、出力のリストを返す関数。たとえば、唯一の入力を複製する関数にはアリティ1があり、2つの入力を交換する関数にはアリティ2があります。
- ブランクはすべて同一であり、機能以外の目的はありません。
シフトプログラムは0個以上のコマンドで構成され、各コマンドは単一のASCII文字です。合計で8つのコマンドがあります。
!
(適用)スタックから関数f
と値をポップしx
、に適用f
しx
ます。場合はf
アリティ1を持って、リストにはf(x)
、スタックの先頭に追加されます。アリティがあるn > 1
場合、新しい(n-1)
-ary関数g
がスタックにプッシュされます。入力を受け取り、戻ります。x1,x2,...,xn-1
f(x,x1,x2,...,xn-1)
?
(blank)スタックに空白をプッシュします。+
(clone)は、入力を複製する単項関数をスタックにプッシュします。任意の値x
がにマップされ[x,x]
ます。>
(shift)はn
-ary関数を受け取る単項関数をスタックにプッシュし、最初の引数を無視し、残りの引数を呼び出し、結果の前にタックf
する(n+1)
-ary関数g
を返します。たとえば、は入力を受け取ってを返すバイナリ関数です。x
f
x
shift(clone)
a,b
[a,b,b]
/
(fork)は、3つの入力を受け取る3項関数をスタックにプッシュし、が空白の場合はそれ以外a,b,c
を返します。[b]
a
[c]
$
(呼び出し)は、関数f
と値をポップするバイナリ関数をスタックにプッシュし、そうx
するのf
とx
まったく同じように適用され!
ます。.
(鎖)スタックにプッシュ二つの機能をポップバイナリ関数f
とg
し、その組成を返す:関数h
と同じアリティを有しf
、通常はその入力をとるには、適用されるf
それらにし、次いで完全適用g
結果に(コールそのアリティが指示する回数だけ)、の出力からの未使用のアイテムはの結果にf
残りh
ます。たとえば、それf
が2番目の引数を複製するバイナリ関数で、g
がcallであるとします。スタックが含まれている場合は[f,g,a,b,c]
、私たちが行う.!!
、それが含まれています[chain(f,g),a,b,c]
。!!
次に行う場合、f
最初にに適用されa,b
、[a,b,b]
、それからg
その最初の2つの要素に適用され[a(b),b]
ます[a(b),b,c]
。そのアリティは2であるため、生成し、スタックは最終的にになります。@
(言う)単純に入力を返す単項関数をプッシュし、0
それが空白1
か、関数かを出力します。
!
値をスタックにプッシュする以外のすべてのコマンドは、入力を実行する方法がなく、何かを出力する唯一の方法はを使用すること@
です。プログラムは、コマンドを1つずつ評価し、「say」が呼び出されるたびに0
sまたは1
sを出力して終了することによって解釈されます。ここで説明されていない動作(空白の適用、長さ0または1のスタックの適用、空白での「チェーン」の呼び出しなど)は定義されていません。インタープリターがクラッシュしたり、警告なしに失敗したり、入力を要求したりする場合があります。
タスク
あなたの仕事はシフトの通訳を書くことです。STDIN、コマンドライン、または関数の引数から解釈されるShiftプログラムを取得し、STDOUTに出力するか、0
sおよび1
sの結果の出力(おそらく無限)を返す必要があります。関数を作成する場合、何らかの方法(Pythonのジェネレーター、Haskellの遅延リストなど)で無限長の出力にアクセスできる必要があります。または、別の入力である数値を受け取り、それよりも長い場合はn
少なくともn
出力の文字を返すことができますn
。
最も低いバイト数が優先され、標準の抜け穴は許可されません。
テストケース
このシフトプログラムは次のように出力します01
。
?@!@@!
左から:空白を押し、「発言」を押してから、発言を空白に適用します。これは出力します0
。次に、sayを 2回押して、2番目の発言を最初の発言に適用します。これは出力します1
。
このプログラムは永久にループし、出力を生成しません。
$+.!!+!!
プッシュ呼び出しとクローンを作成し、それらにチェーンを適用します(チェーンはバイナリ関数である!
ため、2つのが必要です)。これで、スタックには1つの引数を取り、それを複製して、2番目の最初のコピーを呼び出す関数が含まれています。では、この関数を複製してそれ自体を呼び出します。+!!
このプログラムは印刷します0010
:
?@$.++>!.!!.!!.!!!!+?/!!!@!@>!!!
空白を押して言ってください。次に、2番目の引数をコピーするバイナリ関数を作成してからb
、最初の引数をコピーしa
てそれ自体で作成し、次にそのコピーをのコピーに適用してb
、を返し[a(a(b)),b]
ます。適用言うと、ブランク、その後、適用発言権をスタック上に残りの二つの要素に。
このプログラムは印刷し0
ます。!!!
追加したそれぞれについて、追加のを出力し0
ます。
?@+$>!>!+>!///!!>!>!.!!.!!.!!+!!!!
空白を押して言ってください。次に、f,g,x
入力としてを返す3項関数を作成します[f,f,g,g(x)]
。その関数のクローンを作成し、それ自体、たとえば、および空白に適用します。このアプリケーションはスタックを変更しないため、必要な回数だけ関数を再度適用できます。
このプログラムは無限シーケンスを出力します。s 001011011101111...
の数は1
常に1ずつ増加します。
@?/!@>!??/!!>!+.!!.!!.!!.+>!.!!$$$$+$>!>!$>!>!+>!$>!>!>!+>!>!///!!>!>!>!.!!.!!.!!.!!.!!.!!.!!.!!.!!.!!+!!!!!
リポジトリには注釈付きのバージョンが含まれています。
f(x1, x2, ..., xn)
としg(y1, y2, ..., ym)
ます。呼び出す.
と、両方がポップされ、関数がプッシュされますh(z1, z2, ..., zn)
。これで、で徐々にカレー化することにより、これらすべての議論を食い尽くすことができます!
。このn
ようなアプリケーションの後、残りの関数には引数が1つしかなく、その時点で計算f(z1, z2, ..., zn)
(つまりf
、カリー化したすべての引数に適用)が行われ、いくつかの新しい値がプッシュされ、すぐm
にスタックから値が消費さg
れて呼び出されます。
.
は、Martinの説明とまったく同じように機能しf
ます。ただし、m
値よりも小さいリストを返す場合、結果は未定義です(コンポジションにはarity n
があるため、スタックからさらに引数を食べることはできません)。本質的に、出力f
れた一時的なスタックとして使用され、g
押されて印加されるm
時間は、使用!
、及びその結果は、メインスタックに追加されます。