4

古い Java/Python の頭を clojure のようにひねっています。clojureの遅延機能を理解するのを手伝ってください。

=> (def myvar (lazy-seq [1 2 (prn "abc")]))
#'user/myvar

以上が分かりやすいです。これは遅延シーケンスであるため、 (prn "abc") は評価されず、何も出力されません。

=> (def myvar (lazy-seq [1 2 (prn undefined-var)]))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: undefined-var in this context, compiling:(NO_SOURCE_PATH:1) 

ご覧のとおり、上記はエラーを発生させます。なんで ?

私の(間違った)理解は、怠惰なので、「undefined-var」がまだ定義されていなくても、(prn undefined-var)が合法的にここにある可能性があるということです。

誰でも私の理解を正しい方法に向けてください。

4

3 に答える 3

9

Clojureリーダーが見つけたとき

 (def myvar (lazy-seq [1 2 (prn undefined-var)]))

undefined-var が定義されていないため、コンパイルする必要があります。これがエラーをスローする理由です。最初のケースでは、問題なくコンパイルされますが、seq を消費するまで実行されません。

于 2012-06-29T00:20:50.070 に答える
8

上記の両方の回答は、この件に関する優れた情報を提供しますが、ここで重要な問題を突き止めようとします. REPL にs 式を書くと、次の(+ x 2)2 つのことが起こります。

  1. シンボルなどのフォームを生成する文字のリーダーによる解析
  2. フォームの評価。

遅延評価は 2 番目のステップを先延ばししますが、最初のステップでリーダーが遭遇undefined-varすると、それをシンボルに変換しようとし、そのようなシンボルが定義されていないことを発見します。

于 2012-06-29T09:22:00.573 に答える
1

あなたは正しい理解を持っているようですが、ここでlazy-seq 関数がどのように機能するか は典型的な例ではありません:

user> (lazy-seq (cons 4 (range 5)))
(4 0 1 2 3 4)

lazy-seqほとんどの場合、最初の引数がシーケンスの最初の項目で、2 番目の引数がリストの残りを生成するコードである cons 式を取ります。日常の Clojure で lazy-seq を直接使用する必要はめったになくmap reducefilter、 などの形式を使用する方がはるかに一般的です。

eval残りのシーケンスを生成するための関数のコードもコンパイルできる必要があります。あなたの場合、他の人が読む必要があるコードでこのようなものに eval を使用しないことをお勧めしますが、使用することでコンパイルを遅らせることができます。しかし、素晴らしい学習になります;-)

user> (def myvar (lazy-seq (cons 1 (eval '(prn undefined-var))))) ; don't do this at work
#'user/myvar
user> myvar
; Evaluation aborted.
user> 
于 2012-06-29T00:16:42.573 に答える