Я пытаюсь понять, что является идиоматическим способом в Clojure для рекурсии через дерево или список, представленный списком Clojure (или другим типом коллекции).
Я мог бы написать следующее, чтобы подсчитать элементы в плоской коллекции (игнорируйте тот факт, что он не является хвостовым рекурсивным):
(defn length
([xs]
(if (nil? (seq xs))
0
(+ 1 (length (rest xs))))))
Теперь в схеме или 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))для проверки атомов