2016-03-11 5 views
1

я в настоящее время переживаю книги Clojure для храброго и Истинного, в попытке выучить язык, но я немного повесил на ленивых seqs, и я боюсь, the book does a poor job explaining them , Но, согласно книге, что-то вроде этого:Ленивый seqs не отложив вычисление

(defn wait-for-a-bit [arg] 
    (Thread/sleep 1000)) 

(defn get-map [seq] 
    (map wait-for-a-bit seq)) 

(time (first (get-map [1 2 3 4 5 6 7 8 9 0]))) 

Если взять только около одной секунды, чтобы обработать, так как значение для ленивой SEQ не рассчитываются (реализованная?), Пока он не доступен. Однако, когда я запускаю вышеуказанный код, это занимает около десяти секунд, поэтому ясно, что отложенные вычисления не происходят. Я просмотрел документы по адресу clojuredocs.org, и я думаю, что я понимаю lazy-seq, но я думаю, что просто не в контексте карты, уменьшения и т. Д.

+0

Я видел похожие. то я попробовал '(time (first (get-map (iterate inc 1000)))), который дал мне более интуитивный ответ в 1 секунду. – lispHK01

ответ

3

По ленивым последовательностям чередуются последовательности. Таким образом, фактические значения вычисляются в кусках по 30 или около того. Это значительно снижает затраты на переключение контекста при их обработке.

здесь я определить последовательность длиной 100 пунктов и посмотреть на первых двух пунктов:

hello.core> (def foo (map #(do (println "calculating " %) 
           %) 
          (range 100))) 
#'hello.core/foo 
hello.core> (first foo) 
calculating 0 
calculating 1 
calculating 2 
calculating 3 
calculating 4 
calculating 5 
calculating 6 
calculating 7 

... 

calculating 26 
calculating 27 
calculating 28 
calculating 29 
calculating 30 
calculating 31 
0 
hello.core> (second foo) 
1 

Это показывает, что он вычисляет первый блок в первый раз любой из элементов реализуются.

Некоторые последовательности фрагментированы, а другие нет. Это зависит от функции, изначально создающей seq, чтобы решить, можно ли ее разбить. range создает последовательности каналов, а iterate - нет. Если мы еще раз посмотрим на тот же самый пример, на этот раз генерируя с послед, используя итерацию, а не карту, мы получаем не-блочной последовательности:

hello.core> (def foo (map #(do (println "calculating " %) 
           %) 
          (take 100 (iterate inc 0)))) 
#'hello.core/foo 
hello.core> (first foo) 
calculating 0 
0 
hello.core> (second foo) 
calculating 1 
1 

и каждый элемент вычисляется, когда он будет читать. Теоретически это влияет на эффективность, хотя я пишу Clojure полный рабочий день примерно до тех пор, пока кто-то и никогда не видел случая, когда это делало разницу в чем-то, что иначе не было плохо спроектировано.

+0

Несомненно, это «карта», где это возможно, как [исходный код] (https://github.com/clojure/clojure/blob/clojure-1.7.0/src/clj/clojure/core.clj#L2596), а не 'lazy-seq' в целом. – Thumbnail

+0

Карта (и большинство функций, которые преобразуют последовательности) сохраняют неряшливость, если она уже существует, хотя они не будут добавлять ее там, где она не находится в исходной последовательности. Существует несколько типов, которые реализуют ленивые последовательности, некоторые из них чередуются, а другие - –