質問
スキームのコール/CCのセマンティクスを把握しようとしています。継続に関するウィキペディアページは、Yin-Yangパズルを例として示しています。
(let* ((yin
((lambda (cc) (display #\@) cc) (call-with-current-continuation (lambda (c) c))))
(yang
((lambda (cc) (display #\*) cc) (call-with-current-continuation (lambda (c) c)))) )
(yin yang))
出力する必要があります @*@**@***@****@...
, 、しかし、私は理由がわかりません。出力が期待されます @*@*********
...
陰陽パズルがなぜ機能するのかを詳細に説明できますか?
解決
私はこれを完全に理解しているとは思わないが、私はそれを考えることができるだけだ非常に 手波)これの説明:
- 最初の @と *は、いつ印刷されます
yin
とyang
最初にバインドされていますlet*
.(yin yang)
適用され、最初のコール/CCが終了した直後に、トップに戻ります。 - 次の @と *が印刷され、次に別の *が印刷されます。
yin
2回目のコール/CCの値に再バウンドされています。 (yin yang)
もう一度適用されますが、今回は オリジナルで実行されていますyang
'の環境, 、 どこyin
最初のコール/CCにバインドされるため、制御は別の @を印刷することに戻ります。yang
引数には、2回目のパスで再キャプチャされた継続が含まれています。**
. 。したがって、この3番目のパスでは、@*
印刷され、このダブルスタープリントの継続が呼び出されるため、最終的には3つ星が付いています。
他のヒント
理解スキームを理解します
このパズルを理解することで問題の少なくとも半分はスキームの構文であり、ほとんどがよく知られていないと思います。
まず第一に、私は個人的にそれを見つけます call/cc x
同等の代替品よりも理解するのが難しいため、 x get/cc
. 。それはまだ Xを呼び出し、現在の継続を渡します, 、しかし、どういうわけか私の脳回路で表現されることがより適切です。
それを念頭に置いて、構成要素 (call-with-current-continuation (lambda (c) c))
単純になります get-cc
. 。私たちは今これにあります:
(let* ((yin
((lambda (cc) (display #\@) cc) get-cc))
(yang
((lambda (cc) (display #\*) cc) get-cc)) )
(yin yang))
次のステップは、内側のラムダの体です。 (display #\@) cc
, 、より馴染みのある構文で(とにかく)意味します print @; return cc;
. 。私たちがそれに取り組んでいる間、書き直しましょう lambda (cc) body
なので function (arg) { body }
, 、括弧の束を削除し、関数呼び出しをcのような構文に変更して、これを取得します。
(let* yin =
(function(arg) { print @; return arg; })(get-cc)
yang =
(function(arg) { print *; return arg; })(get-cc)
yin(yang))
今はもっと理にかなっています。これを完全に書き直すための小さなステップになりました。
var yin, yang;
yin = (function(arg) { print @; return arg; })(get-cc);
yang = (function(arg) { print *; return arg; })(get-cc);
yin(yang);
最も難しい部分は終わりました、私たちはスキームからこれを解読しました!冗談だ;スキームの経験がなかったので、それは大変でした。それでは、これが実際にどのように機能するかを考えてみましょう。
継続の入門書
陰と陽の奇妙に定式化されたコアを観察する:それは関数を定義します そして、すぐにそれを呼びます. 。まるで似ています (function(a,b) { return a+b; })(2, 3)
, 、それを単純化することができます 5
. 。しかし、Yin/Yang内のコールを簡素化するのは間違いです。なぜなら、私たちはそれを通常の価値を渡していないからです。関数を渡します 継続.
継続は、一目ぼれが奇妙な獣です。はるかにシンプルなプログラムを考えてみましょう。
var x = get-cc;
print x;
x(5);
最初は x
現在の継続オブジェクトに設定されています(私と一緒に熊)、そして print x
実行されて実行されます <ContinuationObject>
. 。ここまでは順調ですね。
しかし、継続は関数のようなものです。 1つの引数で呼び出すことができます。それがすることは:議論をして、そして ジャンプ その継続が作成された場所では、すべてのコンテキストを復元し、それを作る get-cc
この議論を返します。
私たちの例では、議論はです 5
, 、だから私たちは本質的にその真ん中に戻ってジャンプします var x = get-cc
声明、今回だけ get-cc
戻り値 5
. 。それで x
なります 5
, 、そして次のステートメントは5を印刷します。その後、私たちは電話をかけようとします 5(5)
, 、これはタイプエラーであり、プログラムはクラッシュします。
継続を呼び出すことはaです ジャンプ, 、電話ではありません。継続が呼ばれた場所に戻ることはありません。それは重要です。
プログラムの仕組み
あなたがそれに従っていれば、あなたの希望を得ないでください:この部分は本当に最も難しいです。これが再び私たちのプログラムです。これはとにかく擬似コードであるため、変数宣言をドロップします。
yin = (function(arg) { print @; return arg; })(get-cc);
yang = (function(arg) { print *; return arg; })(get-cc);
yin(yang);
最初のタイムライン1と2がヒットします、それらは今シンプルです:継続を取得し、関数(arg)を呼び出し、印刷 @
, 、返品、その継続を保存します yin
. 。と同じ yang
. 。私たちは今印刷しました @*
.
次に、継続を呼び出します yin
, 、それを渡します yang
. 。これにより、そのget-ccのすぐ内側に1行目にジャンプし、戻ります yang
代わりは。の値 yang
これで、印刷する関数に渡されます @
, 、その後、値を返します yang
. 。今 yin
その継続が割り当てられています yang
もっている。次に、2行目に進みます。C/Cを取得し、印刷 *
, 、c/cを入れます yang
. 。私たちは今持っています @*@*
. 。そして最後に、3行目に行きます。
それを覚えておいてください yin
現在、2行目が最初に実行されたときから継続があります。だから私たちは2行目にジャンプして、すぐに印刷します *
および更新 yang
. 。私たちは今持っています @*@**
. 。最後に、電話してください yin
再び継続します。これは1行目にジャンプし、印刷 @
. 。等々。率直に言って、この時点で私の脳はOutFmemoryの例外を投げ、私はすべてを追跡します。しかし、少なくとも私たちはしました @*@**
!
明らかに、これは従うのが難しく、説明がさらに難しいです。これを行うのに最適な方法は、継続を表すことができるデバッガーに足を踏み入れることですが、悲しいかな、私は何も知りません。私はあなたがこれを楽しんだことを願っています。私は確かに持っています。
最初に黙想し、最後に考えられる答え。
コードはこのように書き直すことができると思います:
; call (yin yang)
(define (yy yin yang) (yin yang))
; run (call-yy) to set it off
(define (call-yy)
(yy
( (lambda (cc) (display #\@) cc) (call/cc (lambda (c) c)) )
( (lambda (cc) (display #\*) cc) (call/cc (lambda (c) c)) )
)
)
または、何が起こっているのかを見るのに役立ついくつかの追加のディスプレイステートメントがあります。
; create current continuation and tell us when you do
(define (ccc)
(display "call/cc=")
(call-with-current-continuation (lambda (c) (display c) (newline) c))
)
; call (yin yang)
(define (yy yin yang) (yin yang))
; run (call-yy) to set it off
(define (call-yy)
(yy
( (lambda (cc) (display "yin : ") (display #\@) (display cc) (newline) cc)
(ccc) )
( (lambda (cc) (display "yang : ") (display #\*) (display cc) (newline) cc)
(ccc) )
)
)
またはこのように:
(define (ccc2) (call/cc (lambda (c) c)) )
(define (call-yy2)
(
( (lambda (cc) (display #\@) cc) (ccc2) )
( (lambda (cc) (display #\*) cc) (ccc2) )
)
)
考えられる答え
これは正しくないかもしれませんが、試してみます。
重要なポイントは、「呼ばれる」継続は、他に何も起こらなかったかのように、「継続」がスタックを以前の状態に戻すということだと思います。もちろん、表示して監視していることはわかりません @
と *
文字。
最初に定義します yin
継続すること A
それはこれを行います:
1. restore the stack to some previous point
2. display @
3. assign a continuation to yin
4. compute a continuation X, display * and assign X to yang
5. evaluate yin with the continuation value of yang - (yin yang)
しかし、私たちが呼ぶ場合 yang
継続、これは起こります:
1. restore the stack to some point where yin was defined
2. display *
3. assign a continuation to yang
4. evaluate yin with the continuation value of yang - (yin yang)
ここから始めます。
初めてあなたが得る yin=A
と yang=B
なので yin
と yang
初期化されています。
The output is @*
(両方 A
と B
継続が計算されます。)
今 (yin yang)
として評価されます (A B)
初めて。
私たちは何を知っています A
します。これを行います:
1. restores the stack - back to the point where yin and yang were being initialised.
2. display @
3. assign a continuation to yin - this time, it is B, we don't compute it.
4. compute another continuation B', display * and assign B' to yang
The output is now @*@*
5. evaluate yin (B) with the continuation value of yang (B')
今 (yin yang)
として評価されます (B B')
.
私たちは何を知っています B
します。これを行います:
1. restore the stack - back to the point where yin was already initialised.
2. display *
3. assign a continuation to yang - this time, it is B'
The output is now @*@**
4. evaluate yin with the continuation value of yang (B')
スタックがどこで復元されたので yin=A
, (yin yang)
として評価されます (A B')
.
私たちは何を知っています A
します。これを行います:
1. restores the stack - back to the point where yin and yang were being initialised.
2. display @
3. assign a continuation to yin - this time, it is B', we don't compute it.
4. compute another continuation B", display * and assign B" to yang
The output is now @*@**@*
5. evaluate yin (B') with the continuation value of yang (B")
私たちは何を知っています B'
します。これを行います:
1. restore the stack - back to the point where yin=B.
2. display *
3. assign a continuation to yang - this time, it is B"
The output is now @*@**@**
4. evaluate yin (B) with the continuation value of yang (B")
今 (yin yang)
として評価されます (B B")
.
私たちは何を知っています B
します。これを行います:
1. restore the stack - back to the point where yin=A and yang were being initialised.
2. display *
3. assign a continuation to yang - this time, it is B'"
The output is now @*@**@***
4. evaluate yin with the continuation value of yang (B'")
スタックがどこで復元されたので yin=A
, (yin yang)
として評価されます (A B'")
.
.......
今はパターンがあると思います。
電話するたびに (yin yang)
のスタックをループします B
継続的に戻るまで yin=A
そして、私たちは表示します @
. 。のスタックをループします B
継続的な書き込みa *
毎回。
(これがほぼ正しいなら、本当に幸せです!)
質問してくれてありがとう。
Yinyang Puzzleはスキームで書かれています。スキームの基本的な構文を知っていると思います。
しかし、私はあなたが知らないと思います let*
また call-with-current-continuation
, 、これらの2つのキーワードについて説明します。
説明 let*
あなたがすでにそれを知っているなら、あなたはスキップすることができます Explain call-with-current-continuation
let*
, 、それはどのように見えますか let
, 、ように行動します let
, 、しかし、その定義された変数を評価します( (yin ...)
と (yang ...)
)1つずつ熱心に。つまり、最初に評価します yin
, 、 そしてより yang
.
詳細については、こちらをご覧ください。Let inスキームを使用します
説明 call-with-current-continuation
あなたがすでにそれを知っているなら、あなたはスキップすることができます Yin-Yang puzzle
.
説明するのは少し難しいです call-with-current-continuation
. 。だから私はそれを説明するために比phorを使っています。
呪文を知っていた魔法使いを画像 call-with-current-continuation
. 。彼が呪文をかけると、彼は新しい宇宙を作り、それに彼自身を送ります。しかし、彼はできました 何もしない 新しい宇宙では、誰かが彼の名前を呼ぶのを待っています。一度 呼ばれました, 、魔法使いは元の宇宙に戻り、貧しい男、「誰か」を手に持って、彼の魔法使いの生活に行きました。呼び出されていない場合、新しい宇宙が終了したとき、ウィザードも元の宇宙に戻りました。
わかりました、より技術的にしましょう。
call-with-current-continuation
関数をパラメーターとして受け入れる関数です。電話したら call-with-current-continuation
関数付き F
, 、それは現在の実行環境を詰め込みます。 current-continuation
, 、パラメーターとして C
, 、そしてそれを機能に送ります F
, 、および実行します F
. 。したがって、プログラム全体がなります (F C)
. 。またはより多くのJavaScriptである: F(C);
. C
関数のように動作します。もしも C
呼び出されません F
, 、それは通常のプログラムです F
戻り値、 call-with-current-continuation
値があります F
'の返品値。しかし、場合 C
パラメーターで呼び出されます V
, 、プログラム全体を再度変更します。プログラムはaに戻ります 州 いつ call-with-current-continuation
呼ばれました。でも今 call-with-current-continuation
値を生成します V
. 。そして、プログラムは続きます。
例を見てみましょう。
(define (f return)
(return 2)
3)
(display (f whatever)) ;; 3
(display (call-with-current-continuation f)) ;; 2
(display (call-with-current-continuation (lambda (x) 4))) ;; 4
最初 display
出力 3
, 、原因の。
しかし、2番目 display
出力 2
. 。なんで?
それに飛び込みましょう。
評価するとき (display (call-with-current-continuation f))
, 、最初に評価します (call-with-current-continuation f)
. 。私たちはそれがプログラム全体をに変更することを知っています
(f C)
の定義を検討します f
, 、持っています (return 2)
. 。評価する必要があります (C 2)
. 。それが時です continuation
呼ばれる。そのため、プログラム全体を変更します
(display (call-with-current-continuation f))
でも今、 call-with-current-continuation
価値があります 2
. 。したがって、プログラムは次のようになります。
(display 2)
陰陽パズル
パズルを見てみましょう。
(let* ((yin
((lambda (cc) (display #\@) cc) (call-with-current-continuation (lambda (c) c))))
(yang
((lambda (cc) (display #\*) cc) (call-with-current-continuation (lambda (c) c)))))
(yin yang))
もっと読みやすくしましょう。
(define (id c) c)
(define (f cc) (display #\@) cc)
(define (g cc) (display #\*) cc)
(let* ((yin
(f (call-with-current-continuation id)))
(yang
(g (call-with-current-continuation id))))
(yin yang))
私たちの脳でプログラムを実行しましょう。
ラウンド0
let*
評価してください yin
最初。 yin
は
(f (call-with-current-continuation id))
したがって、評価します (call-with-current-continuation id)
最初。それは私たちがそれを呼ぶ現在の環境を詰め込みます C_0
タイムラインで他の継続と区別するために、まったく新しい宇宙に入ります。 id
. 。だが id
ただ戻ってきます C_0
.
何を覚えておく必要があります C_0
は。 C_0
このようなプログラムです:
(let* ((yin
(f ###))
(yang
(g (call-with-current-continuation id))))
(yin yang))
###
プレースホルダーであり、将来的にはその価値によって満たされます C_0
取り戻します。
だが id
ただ戻ってきます C_0
. 。電話しません C_0
. 。それが電話した場合、私たちは入力します C_0
'の宇宙。しかし、そうではなかったので、私たちは評価し続けます yin
.
(f C_0) ;; yields C_0
f
のような関数です id
, 、しかし、副作用があります - 出力 @
.
したがって、プログラム出力 @
とさせてください yin
することが C_0
. 。これでプログラムがなります
(let* ((yin C_0)
(yang
(g (call-with-current-continuation id))))
(yin yang))
後 yin
評価されて、評価を開始します yang
. yang
は
(g (call-with-current-continuation id))
call-with-current-continuation
ここでは、名前が付けられた別の継続を作成します C_1
. C_1
は:
(let* ((yin C_0)
(yang
(g ###)))
(yin yang))
###
プレースホルダーです。この継続では、 yin
の価値は決定されます(それが何であるか let*
行う)。私たちはそれを確信しています yin
の価値はです C_0
ここ。
以来 (id C_1)
は C_1
, 、 それで yang
の価値はです
(g C_1)
g
副作用があります - 出力 *
. 。したがって、プログラムはそうします。
yang
の価値は今です C_1
.
今では、表示しています @*
だから今それは次のようになります:
(let* ((yin C_0)
(yang C_1))
(yin yang))
両方として yin
と yang
解決されます、評価する必要があります (yin yang)
. 。これは
(C_0 C_1)
聖なるsh*t!
しかし、最後に、 C_0
呼ばれています。だから私たちはに飛びます C_0
宇宙とこれらのsh*tsについてすべてを忘れてください。二度とこの宇宙に戻ることはありません。
ラウンド1
C_0
一緒に取る C_1
戻る。プログラムは次になります(何を忘れた場合 C_0
立って、それを見に戻ってください):
(let* ((yin
(f C_1))
(yang
(g (call-with-current-continuation id))))
(yin yang))
ああ、私たちはそれを見つけます yin
の価値はまだ決定されていません。だから私たちはそれを評価します。評価の過程で yin
, 、ANを出力します @
なので f
の副作用。そして、私たちは知っています yin
は C_1
今。
評価を開始します yang
, 、出会いました call-with-current-continuation
また。私たちは練習されています。継続を作成します C_2
略です:
(let* ((yin C_1)
(yang
(g ###)))
(yin yang))
そして、私たちはaを表示します *
なので g
実行。そして、私たちはここに来ます
(let* ((yin C_1)
(yang C_2))
(yin yang))
だから私たちは得ました:
(C_1 C_2)
あなたは私たちがどこに向かっているのか知っています。わたしたちは・・・にいくつもりです C_1
'の宇宙。メモリからそれを思い出します(または、Webページからコピーして貼り付けます)。今です:
(let* ((yin C_0)
(yang
(g C_2)))
(yin yang))
で知っています C_1
'の宇宙、 yin
の価値が決定されました。そこで、評価を開始します yang
. 。私たちが練習しているように、私はそれが表示することを直接伝えます *
となります:
(C_0 C_2)
今、私たちは印刷しました @*@**
, 、そして私たちは行くつもりです C_0
と一緒に服用しています C_2
.
ラウンド2
私たちが練習しているように、私たちは「@」を表示することを伝えます。 yin
は C_2
, 、そして新しい継続を作成します C_3
, 、次のことを意味します
(let* ((yin C_2)
(yang
(g ###)))
(yin yang))
そして、私たちは表示します *
, yang
は C_3
, 、そしてそれはなります
(C_2 C_3)
そして、私たちは続けることができます。しかし、私はここで停止します、私はあなたに陰陽パズルの最初のいくつかの出力が何であるかをあなたに示しました。
なぜ *
増加しますか?
今、あなたの頭は詳細に満ちています。私はあなたのために要約をします。
単純化するには、Haskell Like Syntaxを使用します。と cc
略です call-with-current-continuation
.
いつ #C_i#
括弧で囲まれています #
, 、それはここで継続が作成されていることを意味します。 ;
出力を意味します
yin = f cc id
yang = g cc id
yin yang
---
yin = f #C_0# ; @
yang = g cc id
yin yang
---
yin = C_0
yang = g #C_1# ; *
yin yang
---
C_0 C_1
---
yin = f C_1 ; @
yang = g #C_2# ; *
yin yang
---
C_1 C_2
---
yin = C_0
yang = g C_2 ; *
yin yang
---
C_0 C_2
---
yin = f C_2 ; @
yang = g #C_3#; *
yin yang
---
C_2 C_3
---
yin = C_1
yang = g C_3 ; *
yin yang
---
C_1 C_3
---
yin = C_0
yang = g C_3 ; *
yin yang
---
C_0 C_3
慎重に観察した場合、それはあなたには明らかです
- たくさんの宇宙があります(実際には無限)が
C_0
始まった唯一の宇宙ですf
. 。その他は始められますg
. C_0 C_n
常に新しい継続を行いますC_max
. 。その理由はC_0
最初の宇宙ですg cc id
もっている いいえ 実行されました。C_0 C_n
また、表示します@
.C_n C_m
0ではないnが表示されます*
.- 時間によってプログラムが推定されます
C_0 C_n
, 、そして私はそれを証明しますC_0 C_n
ますます他の表現によって分離されており、@*@**@***...
少し数学
推定 (n!= 0)は、すべての継続で最大の数字であり、次に C_0 C_n
呼ばれています。
仮定:いつ C_0 C_n
呼ばれています、 C_n
現在の最大数の継続です。
今 によって作成されます C_0 C_n
このような:
yin = f C_n ; @
yang = g #C_{n+1}#
yin yang
だから私たちはそれを結論付けます:
定理I. if C_0 C_n
呼ばれ、継続を生成します , 、 その中で yin
は C_n
.
次のステップは次のとおりです C_n C_{n+1}
.
yin = C_{n-1}
yang = g C_{n+1} ; *
yin yang
その理由 yin
は C_{n-1}
それはいつですか C_n
作成されていることは従った 定理i.
その後 C_{n-1} C_{n+1}
呼び出されており、いつ知っています C_{n-1}
作成され、それも従いました 定理i. 。だから私たちは持っています C_{n-2} C_{n+1}
.
C_{n+1}
内変動です。だから私たちは2番目の定理を持っています:
定理II。もしも C_n C_m
どれの n < m
と n > 0
呼ばれ、それはなります C_{n-1} C_m
.
そして、私たちは手動でチェックしました C_0
C_1
C_2
C_3
. 。彼らは仮定とすべての定理に従います。そして、私たちはどのように最初に知っています @
と *
創造された。
そのため、以下にパターンを書くことができます。
C_0 C_1 ; @ *
C_[1-0] C_2 ; @ * *
C_[2-0] C_3 ; @ * * *
...
それはそれほど厳しくありませんが、私は書きたいです:
QED
別の答えが言ったように、私たちは最初に簡素化します (call-with-current-continuation (lambda (c) c))
と get-cc
.
(let* ((yin
((lambda (cc) (display #\@) cc) get-cc))
(yang
((lambda (cc) (display #\*) cc) get-cc)) )
(yin yang))
現在、2つのラムダは、副作用に関連する同じ関数にすぎません。それらの関数を呼び出しましょう f
(にとって display #\@
) と g
(にとって display #\*
).
(let* ((yin (f get-cc))
(yang (g get-cc)))
(yin yang))
次に、評価順序を作成する必要があります。明確にするために、すべての評価ステップを明示的にする「ステップ式」を紹介します。まず尋ねましょう:上記の関数には何が必要ですか?
の定義が必要です f
と g
. 。ステップ表現では、書きます
s0 f g =>
最初のステップは、計算することです yin
, 、しかし、それはの評価が必要です (f get-cc)
, 、そして後で必要です get-cc
.
大ざっぱに言えば、 get-cc
「現在の継続」を表す値を提供します。これがそうだとしましょう s1
これが次のステップだからです。だから私たちは書きます
s0 f g => s1 f g ?
s1 f g cc =>
パラメーターはスコープレスであることに注意してください。 f
と g
の s0
と s1
同じではなく、現在のステップ内でのみ使用される必要があります。これにより、コンテキスト情報が明示されます。さて、価値は何ですか cc
?それは「現在の継続」なので、それはちょっと同じです s1
と f
と g
同じ値にバインドされています。
s0 f g => s1 f g (s1 f g)
s1 f g cc =>
一度できます cc
, 、評価できます f get-cc
. 。また、それ以来 f
次のコードでは使用されていません。この値を渡す必要はありません。
s0 f g => s1 f g (s1 f g)
s1 f g cc => s2 g (f cc)
s2 g yin =>
次は似ています yang
. 。しかし今、私たちは渡すもう1つの価値があります: yin
.
s0 f g => s1 f g (s1 f g)
s1 f g cc => s2 g (f cc)
s2 g yin => s3 g yin (s3 g yin)
s3 g yin cc => s4 yin (g cc)
s4 yin yang =>
最後に、最後のステップは適用することです yang
に yin
.
s0 f g => s1 f g (s1 f g)
s1 f g cc => s2 g (f cc)
s2 g yin => s3 g yin (s3 g yin)
s3 g yin cc => s4 yin (g cc)
s4 yin yang => yin yang
これにより、ステップ式の構成が完成しました。スキームに戻すのは簡単です:
(let* ([s4 (lambda (yin yang) (yin yang))]
[s3 (lambda (yin cc) (s4 yin (g cc))]
[s2 (lambda (yin) (s3 yin ((lambda (cc) (s3 yin cc))))]
[s1 (lambda (cc) (s2 (f cc)))])
(s1 s1))
詳細な評価順序(ここでは、 s2
部分的な評価として単に表現されました s3 yin
それよりも (lambda (cc) (s3 yin cc))
):
(s1 s1)
=> (s2 (f s1))
=> @|(s2 s1)
=> @|(s3 s1 (s3 s1))
=> @|(s4 s1 (g (s3 s1)))
=> @*|(s4 s1 (s3 s1))
=> @*|(s1 (s3 s1))
=> @*|(s2 (f (s3 s1)))
=> @*@|(s2 (s3 s1))
=> @*@|(s2 (s3 s1))
=> @*@|(s3 (s3 s1) (s3 (s3 s1)))
=> @*@|(s4 (s3 s1) (g (s3 (s3 s1))))
=> @*@*|(s4 (s3 s1) (s3 (s3 s1)))
=> @*@*|(s3 s1 (s3 (s3 s1)))
=> @*@*|(s4 s1 (g (s3 (s3 s1))))
=> @*@**|(s4 s1 (s3 (s3 s1)))
=> @*@**|(s1 (s3 (s3 s1)))
=> ...
(評価するときは覚えておいてください s2
また s4
, 、パラメーターが最初に評価されます
これは、非lambdaを作成した難読化のマスターデビッドマドールからの古いパズルです。パズルについては、comp.lang.schemeについて何度か議論されています。
テイラー・キャンベルからの素晴らしいソリューション:https://groups.google.com/d/msg/comp.lang.scheme/puedvrkyy5w/uijtc_t1loej
David Madore(1999)からの元の投稿:https://groups.google.com/d/msg/comp.lang.scheme/fysq_wplxsw/awxez_uxw20j