Что случилось с ленивой оценкой Clojure
Я скручиваю свою старую голову 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)
Выше будет возникать ошибка, как вы можете видеть. Зачем?
Мое (неправильное) понимание состоит в том, что, поскольку это лениво, (prn undefined-var) может быть здесь юридически, даже "undefined-var" еще не был определен.
Пожалуйста, кто-нибудь направить мое понимание на правильный путь.
3 ответа
Оба приведенных выше ответа дают вам хорошую информацию по этому вопросу, но я постараюсь определить ключевую проблему здесь. Когда вы пишете s-выражение на REPL, как (+ x 2)
происходят две вещи:
Ленивая оценка откладывает второй шаг, но на первом этапе, когда читатель встречает undefined-var
он пытается преобразовать его в символ и обнаруживает, что такой символ не был определен.
Когда читатель clojure находит
(def myvar (lazy-seq [1 2 (prn undefined-var)]))
Он должен скомпилировать его, вот почему он выдает ошибку, поскольку undefined-var не определен. В первом случае он компилируется нормально, но не выполняется, пока вы не используете seq.
Похоже, вы понимаете правильно, просто вот как работает функция lazy-seq, это типичный пример:
user> (lazy-seq (cons 4 (range 5)))
(4 0 1 2 3 4)
lazy-seq
почти всегда принимает выражение "против", где первый аргумент является первым элементом в последовательности, а второй аргумент является кодом для создания остальной части списка. Людям очень редко приходится использовать Lazy-Seq непосредственно в повседневной Clojure, используя такие формы, как map
reduce
, filter
и т. д. гораздо чаще встречаются.
код в функции для создания остальной части последовательности также необходим для возможности компиляции, в вашем случае вы также можете отложить компиляцию, используя 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>