2013-02-22 5 views
8

私はスキームの最初のプログラムを書いています。私は基本的にはネストされた手続き呼び出しを持つことができる単純なロボットのためのプログラムを解釈するので、再帰にかなり深く入る。スキームの再帰を中断する標準的な方法

違反が見つかった場合は、プログラムの解釈を停止し、最後の有効な状態を返す必要があります。

グローバル変数(define illegalMoveFlag 0)を宣言してからset!で設定して解決しました。私が考えた

それは正常に動作しますが、私は(それは私が推測する機能的なアプローチではないので)私の家庭教師がそれを好きではないだろうと思い

他のアプローチは、私は再帰的に呼び出すすべての関数にエラーパラメータを追加することですプログラムで。私はそれが私のコードをはるかに読みにくくするので、それは本当に好きではありませんが、私はそれがより機能的だと思います。

私は考えなかった可能性がありますか?そして私のアプローチはこのパラダイムで正当化できるのでしょうか、それとも基本的にコードのにおいですか?

答えて

5

これは最初のSchemeプログラムだったので、最後に到達したときにさらに再帰するのを避けるために、条件式condを導入するだけでよいでしょう。例:あなたがCでlongjmpのように戻るには、伝統的なreturnが必要な場合は

; sum : natural -> natural 
; compute the sum 0+1+...+max 
(define (sum max) 
    (define (sum-helper i sum-so-far) 
    (if (> i max) 
     sum-so-far 
     (sum-helper (+ i 1) (+ sum-so-far i)))) 
    (sum-helper 0 0)) 

(display (sum 10)) 
(newline) 

しかし、あなたはエスケープ継続を保存して使用する必要があります。

(define (example) 
    (let/ec return 
    (define (loop n) 
     (if (= n 100000) 
      (return (list "final count: " n)) 
      (loop (+ n 1)))) 
    (loop 0))) 

(display (example)) 

let/ecがあなたのSchemeの実装で定義されていない場合は、を使用して、プログラムを接頭辞:

(define-syntax let/ec 
    (syntax-rules() 
    [(_ return body ...) 
    (call-with-current-continuation 
     (lambda (return) 
     body ...))])) 

UPDATE:

cond=>バリアントを持っていることをこれは次のように行うことができます:

(cond 
    [(call-that-can-fail) 
    => (lambda (a) <use-a-here>))] 
    [else <do-something-else>]) 

llが成功すると、最初の句は になり、結果はaにバインドされます。呼び出しが失敗した場合は、 、次にelse節が使用されます。 0:

+0

これまでのところ最高の答えです。しかし、私は簡単に最初の方法を使用することはできません。より具体的には、私は次のようなものを使用します(let(a(call-that-c​​an-fail))(b(other-call-that-c​​an-fail a)))...)) bを実行するのを止めてください(私はそれを条件で行うことができますが、私の現在の解決策よりも醜いでしょう)。私は継続を使用すると思います、それはグローバル変数を持つ私の解決策ではない私の関数を再入可能にするでしょう。 –

+0

'cond'に' => '句を使用できますか?上記の例を参照してください。 – soegaard

5

再帰を停止する通常の方法は、再帰を停止することです。すなわち、もはや再帰関数を呼び出さない。 :-)

これが難しいのであれば、何かを打ち破るもう1つの方法は、(再帰を開始する前に)トップレベルで継続をキャプチャし、次にエスケープする必要があるときに継続を呼び出すことです。あなたのインストラクターはこのアプローチを好まないかもしれません。そのようにあなたは、組込みプロシージャerrorを使用する場合があります;-)

2

(error "Illegal move") ; gives ** Error: Illegal move 

これは例外を発生し、プログラムを解釈しなくなりますが(私はこれがあなたが何であるかではないかもしれない疑いがあるものの探している)。

ます。また、このように、追加の引数を提供することができます。

(error "Illegal move: " move) ; gives ** Error: Illegal move: <move> 
+0

私のプログラムは自動的に評価され、これは仕様を破るので、これは私を助けませんね。 –

1

することはでき継続を使って再帰(または任意の他のプロセスから)の終了。より詳細なことがわからなければ、あなたの通訳者のdocumentationを見てみることをお勧めします。

+0

これは面白そうだ、それについて知らなかった:)しかし、それはやや普通のアプローチですか?それとも、Cでgotoを使うのが好きですか? –

+3

@HonzaBrabec継続は非常に一般的かつ柔軟なフロー制御メカニズムです。例外、 "return"キーワード、yes、[goto](https://en.wikipedia.org/wiki/Goto#Continuations)は、継続の観点から実装できます。しかし、私は問題を解決するためにそれを_普通の方法とは呼んでいません:) –

1

メイクは、私はあなたに階乗

すなわちとの簡単な例をあげる

グローバル変数の代わりに、関数内でPARAMTERをillegalMoveFlag! = 1 n! = n *(n-1)! (1 ...無限大)をn個のとき

は代替が再帰 を終了する関数にパラメータを使用することであろう再帰的な階乗

(define (fact-r n) 
    (if 
     [eq? n 0] 
     1 
     (* n (fact-r (- n 1))) 
    ) 
) 

反復階乗それを呼び出すことができますこれを呼び出すことができます私たちはよりよい

(define (nice-fact n) 
    (fact-i n 1)) 
0を、それを使用して作るために別の関数を作るべき1で開始する
(define (fact-i n total) 
    (if 
     (eq? n 0) 
     total 
     (fact-i (- n 1) (* n total)) 
) 
) 

総ニーズ

illegalMoveFlagで似たようなことをして、グローバル変数を避けることができます setを使用することを避けてください!もっと情報が必要になるだろう。

場合によっては、それを使用することを避けることはまだかなり困難です。スキームはセットを使わずに完全にチューリングされています!ただし、データベースやロボットなどの外部情報源にアクセスすることになります。唯一の実用的なソリューションになることができます...

関連する問題