Это не ответ на ваш вопрос, но в интересах написания «красивого» кода clojure я хотел бы отметить несколько вещей на вашем примере.
Одним из преимуществ функционального стиля является возможность составлять функции вместе. Но если вы посмотрите на написанные вами функции, они по отдельности мало что делают без зависимости от функциональности, предоставленной в другом месте.
Например, stream-builder
просто возвращает двухэлементный список n
и анонимную функцию для обработки псевдорекурсии.
Я предполагаю, что вы пытались использовать ленивую последовательность, но для этого нужно, чтобы вспомогательные функции знали о деталях реализации для реализации следующего элемента, а именно stream
и second$
. К счастью, вместо этого вы можете сделать это следующим образом:
(defn stream-builder [f x] ; f is idiomatic for a function arg
(lazy-seq ; n is idiomatic for a count, so use x instead
(cons x
(stream-builder f (f x)))))
Вышеприведенное фактически возвращает бесконечную последовательность значений, поэтому нам нужно вытащить ограничивающее поведение, связанное внутри stream
:
(defn limit [n coll] ; coll is idiomatic for a collection arg
(lazy-seq ; flipped order, fns that work on seqs usually take the seq last
(when (pos? n)
(when-let [s (seq coll)]
(cons (first s)
(limit (dec n) (next s)))))))
Вышеуказанное лениво вернет до n
элементов coll
:
user=> (limit 5 (stream-builder inc 1))
(1 2 3 4 5)
В конце концов, каждая функция хорошо выполняет одну задачу, и ее можно комбинировать с другими функциями:
Наконец, вы определили odd
как ленивую последовательность нечетных целых чисел. Опасность заключается в том, что последовательность будет слипаться по мере того, как она будет реализована (это известно как «удерживание головы» последовательности). Это может излишне занимать лишнюю память для хранения каждого когда-либо реализованного элемента и предотвращает сборку мусора.
Чтобы решить эту проблему, либо не определяйте ленивую последовательность, либо не определяйте ее с помощью лимита, например ::1010 *
(def odds (limit 23 (stream-builder #(+ 2 %) 1)))
Для дальнейшего использования то, что мы только что написали, доступно в базовой библиотеке как iterate
и take
соответственно:
user=> (take 5 (iterate inc 1))
(1 2 3 4 5)