2

スキームで末尾再帰べき乗関数を書くのに問題があります。ヘルパー関数を使用して関数を書きたい。累積値を保持するためのパラメーターが必要であることはわかっていますが、その後は行き詰まっています。私のコードは次のとおりです。

(define (pow-tr a b)
(define (pow-tr-h result)
  (if (= b 0)
     result
     pow-tr a (- b 1))(* result a)) pow-tr-h 1)

コードを編集したところ、動作するようになりました。それは次のとおりです。

(define (pow-tr2 a b)
 (define (pow-tr2-h a b result)
  (if (= 0 b)
    result
    (pow-tr2-h a (- b 1) (* result a))))
 (pow-tr2-h a b 1))

ヘルパー関数がメイン関数と同じパラメーターを持つ必要がある理由を誰かが説明してくれますか? なぜこれが必要なのか考えるのに苦労しています。

4

3 に答える 3

4

「ヘルパー関数はメイン関数と同じパラメーターを持つ必要がある」と述べるのは正しくありません。各反復で変更されるパラメーター (例では、指数と累積結果) のみを渡す必要があります。たとえば、これはベースをパラメーターとして渡さなくても問題なく機能します。

(define (pow-tr2 a b)
  (define (pow-tr2-h b result)
    (if (= b 0)
        result
        (pow-tr2-h (- b 1) (* result a))))
  (pow-tr2-h b 1))

これが機能するのは、内側のヘルパー プロシージャがa、外側のメイン プロシージャで定義されたパラメータを「見る」ことができるためです。ベースは決して変更されないため、渡す必要はありません。これについて詳しく読むには、すばらしいSICP本の「内部定義とブロック構造」というタイトルのセクションをご覧ください。

ヘルパー プロシージャを使用するようになったので、内部プロシージャを明示的にコーディングせずにヘルパーを記述するための非常に便利な構文であるnamedletの使用を開始することをお勧めします。上記のコードは次と同等です。

(define (pow-tr2 a b)
  (let pow-tr2-h [(b b) (result 1)]
    (if (= b 0)
        result
        (pow-tr2-h (- b 1) (* result a)))))
于 2013-09-29T22:33:35.170 に答える
0

同じ名前ですが、同じパラメーターではありません。インタプリタが何をしているかを掘り下げると、「a」が 2 回定義されていることがわかります。ローカル スコープの場合は 1 回ですが、外側のスコープの "a" はまだ記憶されています。インタープリターが関数を呼び出すと、引数の値を仮パラメーターにバインドしようとします。

アルゴル族の言語で行うように、かなり変化する状態を介して値を渡す理由は、状態を変化させないことで、置換モデルを使用してプロシージャの動作を推論できるからです。引数を指定していつでも呼び出された同じプロシージャは、同じ引数を指定して他の場所から呼び出された場合と同じ結果をもたらします。

純粋に関数型のスタイルでは、値が変わることはなく、新しい値で関数を呼び出し続けます。コンパイラは、スタック上の値を更新するタイトなループでコードを記述できる必要があります (テール コールの削除)。このようにして、人間のコンパイラーのように振る舞うのではなく、アルゴリズムの正確性についてもっと心配することができます。

于 2013-10-01T15:23:15.763 に答える