具体的なケースでは、通常の処理から「エスケープ」する必要はありません。末尾に'(Adding Zero)
と入力すると、add
関数は(Adding Zero)
を返します。
あなたがエスケープする必要があるかもしれませんな状況を作成するには、 もう少し複雑なものが必要:
(define (recursive-find/collect collect? tree (result null))
(cond ((null? tree) (reverse result))
((collect? tree) (reverse (cons tree result)))
((not (pair? tree)) (reverse result))
(else
(let ((hd (car tree))
(tl (cdr tree)))
(cond ((collect? hd)
(recursive-find/collect collect? tl (cons hd result)))
((pair? hd)
(recursive-find/collect collect? tl
(append (reverse (recursive-find/collect collect? hd)) result)))
(else (recursive-find/collect collect? tl result)))))))
あなたは処理を中止したいと、ツリー内の任意のノードが値を持っていた場合だけ'Hahaha!
を返すと仮定'Joker
。末尾の位置に'Hahaha!
を評価するだけでは、 のrecursive-find/collect
が末尾の位置に常に使用されるとは限らないため、十分ではありません。
スキームは、この目的のための継続を提供する。私の特定の例ではそれを行うための最も簡単な方法は、このように、述語関数から継続を使用することです:
(call/cc
(lambda (continuation)
(recursive-find/collect
(lambda (node)
(cond ((eq? node 'Joker)
(continuation 'Hahaha!)) ;; Processing ends here
;; Otherwise find all the symbols
;; in the tree
(else (symbol? node))))
'(Just 1 arbitrary (tree (stucture) ((((that "has" a Joker in it)))))))))
継続がcall/cc
ブロックの後に起こるだろうさ「の計算の残りの部分」を表し終了する。この場合、スタック内のどこからでもcall/cc
ブロックから脱出することができます。
しかし、継続には、このcall/cc
がプログラムのこの部分を実行した後でも表示されるコードのブロックにジャンプすることを許可するなど、他の奇妙なプロパティもあります。例えばこの場合
(define-values a b (call/cc
(lambda (cc)
(values 1 cc))))
(cc 'one 'see-see)
、cc
を呼び出すとdefine-values
形態に戻ってジャンプし、それぞれone
とsee-see
にa
とb
を再定義します。
ラケットにはフォームから逃れることができますが、そこにジャンプすることはできません "エスケープ継続"(call/ec
またはlet/ec
)があります。この制限と引き換えに、パフォーマンスが向上します。
関数が末尾再帰的である限り、問題はなく、最後の式の値が返されます。 TCOを伴う深い呼び出しスタックはありません。 –
再帰呼び出しはどちらもテールコールなので、SICPが「反復プロシージャ」と呼ぶものです。定数空間を使用し、ループにコンパイルされます。 – molbdnilo