9

Scala 2.8.x では、@tailrecコンパイラが注釈付きメソッドで末尾呼び出しの最適化を実行できない場合にコンパイル時エラーを発生させる新しい注釈 ( ) が追加されました。

に関してClojureに同様の機能はありloop/recurますか?

編集: 私の質問に対する最初の回答(ありがとう、Bozhidar Batsov)を読み、Clojureのドキュメントをさらに検索した後、私はこれに出くわしました:

(recur exprs*)
expr を順番に評価し、並行して、再帰ポイントのバインディングを expr の値に再バインドします。再帰ポイントが fn メソッドの場合、params を再バインドします。再帰ポイントがループの場合は、ループ バインディングを再バインドします。その後、実行は再帰ポイントに戻ります。recur 式は、再帰ポイントのアリティと正確に一致する必要があります。特に、再帰ポイントが可変引数 fn メソッドの先頭にある場合、残りの引数の収集はありません。単一の seq (または null) を渡す必要があります。末尾位置以外での再発はエラーです。

recur は、Clojure でスタックを消費しない唯一のループ構造であることに注意してください。末尾呼び出しの最適化はなく、未知の境界のループに自己呼び出しを使用することは推奨されません。recur は機能的であり、末尾位置での使用はコンパイラによって検証されます[強調は私のものです]。

(def factorial
  (fn [n]
    (loop [cnt n acc 1]
       (if (zero? cnt)
            acc
          (recur (dec cnt) (* acc cnt))))))
4

2 に答える 2

6

実際、Scala wrt Tail Call Optimization の状況は Clojure と同じです。自己再帰などの単純な状況では実行できますが、末尾位置で任意の関数を呼び出すなどの一般的な状況では実行できません。

これは、JVM の動作方法によるものです。TCO が JVM で動作するには、JVM 自体がそれをサポートする必要がありますが、現在はサポートされていません (ただし、これは JDK7 のリリース時に変更される可能性があります)。

TCO と Scala でのトランポリンの議論については、たとえばこのブログ エントリを参照してください。Clojure には、スタックを消費しない (= テールコール最適化された) 再帰を容易にするまったく同じ機能があります。これには、ユーザー コードが末尾以外の位置で呼び出そうとしたときにコンパイル時エラーをスローすることが含まれますrecur

于 2010-04-26T15:29:17.847 に答える
4

ループ/再帰AFAIKを使用する場合、テールコールの最適化はありません。公式ドキュメントからの引用:

変更可能なローカル変数がない場合、ループと反復は、状態の変更によって制御される組み込みの for または while 構造を持つ言語とは異なる形式を取る必要があります。関数型言語では、ループと反復は再帰関数呼び出しによって置き換え/実装されます。そのような言語の多くは、末尾位置で行われた関数呼び出しがスタック スペースを消費しないことを保証するため、再帰ループは一定のスペースを利用します。Clojure は Java 呼び出し規則を使用するため、同じ末尾呼び出しの最適化を保証することはできません。代わりに、recur 特殊演算子を提供します。この演算子は、再バインドして最も近い囲みループまたは関数フレームにジャンプすることにより、定数空間の再帰ループを実行します。tail-call-optimization ほど一般的ではありませんが、

于 2010-04-26T11:14:08.403 に答える