2016-05-22 2 views
2

Почему это работаетленивый след StackOverflowError

(def fibs (cons 0 (cons 1 (lazy-seq (map + fibs (rest fibs)))))) 
(take 10 fibs) 

в то время как этот другой

(def fibs (lazy-seq (cons 0 (cons 1 (map + fibs (rest fibs)))))) 
(take 10 fibs) 

генерировать StackOverflowError? первая нота

+0

Что вы пытаетесь достичь с помощью 'lazy-seq'? Кажется, это нормально, если вы просто удалите его. – jmargolisvt

+0

@jmargolisvt На мой repl (clojure 1.6) код '(def fibs (cons 0 (cons 1 (map + fibs (rest fibs)))))' не работает. Lazy-seq необходимо, я думаю, потому что он задерживает вычисление потока, иначе он не будет работать с определением fibs в терминах самих фибо – Matteo

ответ

4

Давайте, что он также работает с одним cons снаружи:

(def fibs (cons 0 (lazy-seq (cons 1 (map + fibs (rest fibs)))))) 

Давайте также установить, что lazy-seq макрос (очевидно, так как он не оценивает аргументы) и помещает body (аргумент (s) до lazy-seq) в функцию.

Теперь, если вы «запросите» элемент из этой последовательности, эта функция вызывается один раз, а последующие вызовы возвращают кешированное значение.

Очевидно, что только после того, как функция возвращает что-то, у вас есть доступное значение. Теперь, что происходит в нашем плохом случае:

Представьте, что clojure находится в середине оценки функции в первый раз. Первое, что ему нужно, это +, fibs и (rest fibs), чтобы передать это минусам. Теперь clojure увидит, что fibs является ленивой последовательностью и вызывает его! Это потому, что вы в настоящий момент находитесь в своем первом вызове и не возвращаетесь. Это вызывает бесконечную рекурсию.

Исправить легко: убедитесь, что в начале списка есть реализованный элемент, чтобы два выражения, fibs и (rest fibs) могли что-то вернуть. В частности, взятие rest из (cons 1 whatever) вернет whateverwithout realizing a single element (важно, поскольку в противном случае у нас была бы такая же проблема снова).