Я пытаюсь понять, каков идиоматический способ в Clojure проходить через дерево или список, представленный списком Clojure (или другим типом коллекции).
Я мог бы написать следующее для подсчета элементов в плоской коллекции (игнорируя тот факт, что это не хвостовая рекурсия):
(defn length
([xs]
(if (nil? (seq xs))
0
(+ 1 (length (rest xs))))))
Теперь в Scheme или CL все примеры делают это только над списками, поэтому идиоматический тест базового варианта в этих языках будет (nil? xs)
. В Clojure мы хотели бы, чтобы эта функция работала на всех типах коллекций, поэтому идиоматический тест (nil? (seq xs))
или, может быть, (empty? xs)
, или что-то совершенно другое?
Другой случай, который я хотел бы рассмотреть, - это обход дерева, то есть обход списка или вектора, представляющего дерево, например [1 2 [3 4]
.
Например, подсчет узлов в дереве:
(defn node-count [tree]
(cond (not (coll? tree)) 1
(nil? (seq tree)) 0
:else (+ (node-count (first tree)) (node-count (rest tree)))))
Здесь мы используем (not (coll? tree))
для проверки на атомы, тогда как в Схеме / CL мы будем использовать atom?
. Мы также используем (nil? (seq tree))
для проверки пустой коллекции. И, наконец, мы используем first
и rest
для деструктурирования текущего дерева до левой ветви и остальной части дерева.
Итак, подведем итог, следующие идиоматические формы в Clojure:
(nil? (seq xs))
для проверки пустой коллекции
(first xs)
и (rest xs)
копать в коллекцию
(not (coll? xs))
для проверки на атомы