Как сказал @NathanDavis, let
позволяет указывать промежуточные значения:
(loop [k from res '()]
(if (< (count res) n)
(let [next-k (next-num k)]
(recur next-k (conj res next-k)))
(sort res)))
Однако, прежде чем перейти к loop
, стоит посмотреть, сможете ли вы скомпоновать основные функции с тем же эффектом. Часто вы можете написать что-то менее сложное и раскрыть важные детали.
Основная часть кода касается построения последовательности повторяющихся применений next-num
. К счастью, для этого есть основная функция: iterate
. Используя iterate
, мы можем создать бесконечную ленивую последовательность значений:
(iterate next-num from)
; => (from, from', from'', ...)
Однако нам не нужно первое из этих значений. Мы можем получить остальную часть последовательности с помощью rest
:
(rest
(iterate next-num from))
; => (from', from'', from''', ...)
На данный момент мы можем получить n
значений с помощью take
:
(take n
(rest
(iterate next-num from)))
; => (from', from'', from''')
И, наконец, мы можем отсортировать эти значения n
:
(sort
(take n
(rest
(iterate next-num from))))
; => (from'', from', from''')
Конечно, глубоко вложенные вызовы функций вскоре становятся неудобными. Макрос потоковой обработки ->>
(как и его брат ->
) — это немного синтаксического сахара, который позволяет нам преобразовать наш код во что-то более приятное:
(->>
(iterate next-num from)
rest
(take n)
sort)
Итак, вы видите, как мощная библиотека функций управления последовательностями позволяет нам отказаться от низкоуровневых циклов.
person
Beyamor
schedule
18.12.2013