перерыв и отдых - PullRequest
       73

перерыв и отдых

7 голосов
/ 01 ноября 2011

Я следовал Радость Clojure , и я озадачен этими 2 утверждениями

(def very-lazy (-> (iterate #(do (print \.) (inc %)) 1) rest rest rest))

(def less-lazy (-> (iterate #(do (print \.) (inc %)) 1) next next next))

соответственно, результат был

(println (first very-lazy)) ; .4

(println (first less-lazy)) ; 4

Книга продолжаласьчтобы объяснить, что

Получение первого элемента в ленивом seq, построенном с отдыхом, вызывает реализацию, как и ожидалось.Но то же самое не происходит с секвенсом, построенным с next, потому что это уже было реализовано ранее.Использование next приводит к тому, что ленивый seq будет на один элемент менее ленивым, что может быть нежелательно, если стоимость реализации высока.

Мой острый вопрос - почему для «очень ленивых» есть дополнительная точка«?Я думал, что print напечатает аргумент при вызове, независимо от того, next или rest.

Спасибо

Ответы [ 5 ]

7 голосов
/ 01 ноября 2011

Печать фактически делала одно и то же в обоих случаях, она печатала только номер.Дополнительный . был напечатан кодом внутри списка, он произошел одновременно с печатью 4, и поэтому он оказался рядом с ним на экране.

дополнительная точка - побочный эффект ленивой последовательности, создаваемой на лету .Я бы предложил более подробный пример, чтобы прояснить это:

начать с двух одинаковых списков, оба полностью ленивые:

esc.core=> (def a (iterate #(do (print "making new element") (inc %)) 1)) 
#'esc.core/a
esc.core=> (def b (iterate #(do (print "making new element") (inc %)) 1))
#'esc.core/b

, а затем составить еще два идентичных списка, которые начинаются с четвертого элементаa и b соответственно

esc.core=> (def a-partially-realized (-> a rest rest rest))
making new elementmaking new element#'esc.core/a-partially-realised
esc.core=> (def b-more-fully-realized (-> b next next next))
making new elementmaking new elementmaking new element#'esc.core/b-more-fully-realised
esc.core=> 

первые три элемента a-partially-realized были предварительно вычислены
, в то время как первые четыре элементаb-more-fully-realized были предварительно вычислены.

, когда мы читаем первый элемент (четвертый в исходном списке) из a-partially-realized, он еще не был вычислен, поэтому мы увидим, что он вычисляется.

esc.core=> (print (first a-partially-realized))
making new element4nil

когда мы сделаем то же самое с b-more-fully-realised, оно уже будет кэшировано, поэтому мы сразу получим результат.

esc.core=> (print (first b-more-fully-realized))
4nil
5 голосов
/ 01 ноября 2011

Вы упускаете что-то важное. Подсчитайте точки:

user> (def very-lazy (-> (iterate #(do (print \.) (inc %)) 1) rest rest rest))
..#'user/very-lazy

user> (def less-lazy (-> (iterate #(do (print \.) (inc %)) 1) next next next))
...#'user/less-lazy

Всегда помогает запустить код по-настоящему, а не просто читать книгу.

4 голосов
/ 02 ноября 2011

Как указывает Джуст, магия происходит, когда вы определяете очень ленивый и менее ленивый.

Смысл в том, что отдых просто должен возвращать последовательность.Эта последовательность ленива.Следовательно, (-> x rest rest rest) необходимо обработать два значения x и затем вернуть ленивую последовательность.Следовательно, две точки.

next, с другой стороны, должно возвращать nil, если в последовательности больше нет элементов, поэтому необходимо оценить x в третий раз, чтобы определить, была ли последовательность пустой.Итак, три точки.

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

1 голос
/ 01 ноября 2011

То, что до сих пор остается без ответа, это то, почему next более нетерпеливо , чем rest, что лежит в основе разницы, которую вы описываете. next возвращает nil для любого списка длиной менее 2. Напротив, rest возвращает пустой список в этих сценариях. Это говорит о том, что при вызове next для списка из 1 элемента мы неявно завершаем обработку нашей последовательности (например, в рекурсии), и, таким образом, мы можем смело предположить, что пришло время реализовать первый элемент. Это объясняет раннюю и позднюю реализацию финальной точки.

0 голосов
/ 13 июля 2015

next выполняет функцию #(), как только она вызывается, в то время как rest не выполняет, потому что в этом нет необходимости - она ​​ждет как можно дольше - поэтому в этом примере реализация не выполняетсявызывается другой rest, или необходимо вернуть значение, как в случае печати.

Я тоже пропустил точки при первом чтении.Только две точки для rest, потому что не было достаточной причины (пока) для реализации последних rest.

...