2016-03-22 5 views
0

注:可能であれば、例外で構築されたラケットを使用しないでこれを行いたいと思います。再帰を止めラケットで何かを返すには?

私は他の関数を呼び出す多くの関数を持っており、再帰的に呼び出し元の関数に戻ることがあります。途中の特定の条件の下で、私はそれ以上の再帰的なステップを止めたいので、他の関数を呼び出すことなく、単純に値/文字列を返すことはありません(条件が満たされればスタックは無視されます)。私が達成しようとしているものを表示します:

(define (add expr0 expr1) 
(cond 
[(list? expr0) (add (cadr expr0) (cadr (cdr expr0)))] 
[(list? expr1) (add (cadr expr1) (cadr (cdr expr1)))] 
[else (if (or (equal? expr0 '0) (equal? expr1 '0)) 
     '(Adding Zero) 
     (+ expr0 expr1))] 
)) 

これは私の機能だったら、私は単純に文字列全体を返すために、そして、目標は次のようになります((2 0)3を追加)とそれを呼ば"ゼロではなく、への再帰呼び出しすることで、表現の一つである(追加ゼロ) ANYTIME(追加「を(追加ゼロ)3)

は、方法はあります再帰のうち、基本的に 『ブレーク』に?私の問題は、もし私がすでに深いところにいたら、最終的に評価することを試みるでしょう。(ゼロを加えること)それはやり方がわからず、それぞれに明白なチェックをせずにこれを行うことができるはずですexpr ..

いずれかの指針は素晴らしいでしょう。

+2

関数が末尾再帰的である限り、問題はなく、最後の式の値が返されます。 TCOを伴う深い呼び出しスタックはありません。 –

+0

再帰呼び出しはどちらもテールコールなので、SICPが「反復プロシージャ」と呼ぶものです。定数空間を使用し、ループにコンパイルされます。 – molbdnilo

答えて

0

具体的なケースでは、通常の処理から「エスケープ」する必要はありません。末尾に'(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形態に戻ってジャンプし、それぞれonesee-seeabを再定義します。

ラケットにはフォームから逃れることができますが、そこにジャンプすることはできません "エスケープ継続"(call/ecまたはlet/ec)があります。この制限と引き換えに、パフォーマンスが向上します。

関連する問題