Estoy tratando de entender cuál es la manera idiomática en Clojure para recurse a través de un árbol o una lista representada por una lista Clojure (u otro tipo de colección).Manera idiomática de recurse a través de colecciones en Clojure
podría escribir lo siguiente para contar los elementos de una colección plana (ignorar el hecho de que no es recursiva de cola):
(defn length
([xs]
(if (nil? (seq xs))
0
(+ 1 (length (rest xs))))))
Ahora en Scheme o Cl todos los ejemplos sólo alguna vez hacer esto más listas , por lo que la prueba de caso base idiomática en esos idiomas sería (nil? xs)
. En Clojure, nos gustaría que esta función funcione en todos los tipos de colecciones, ¿cuál es la prueba idiomática (nil? (seq xs))
, o quizás (empty? xs)
, o algo completamente diferente?
El otro caso que me gustaría considerar es el recorrido de árbol, es decir, atravesar una lista o un vector que representa un árbol, p. [1 2 [3 4]
.
Por ejemplo, contar los nodos en un árbol:
(defn node-count [tree]
(cond (not (coll? tree)) 1
(nil? (seq tree)) 0
:else (+ (node-count (first tree)) (node-count (rest tree)))))
Aquí se utiliza para comprobar si hay (not (coll? tree))
átomos, mientras que en el Esquema/CL usaríamos atom?
. También usamos (nil? (seq tree))
para verificar si hay una colección vacía. Y finalmente usamos first
y rest
para desestructurar el árbol actual a la rama izquierda y al resto del árbol.
Entonces, para resumir, son las siguientes formas idiomáticas en Clojure:
(nil? (seq xs))
para detectar la colección vacía(first xs)
y(rest xs)
a excavar en la colección(not (coll? xs))
para comprobar si hay átomos
Gracias por su respuesta. Acerca de 'rest' /' next', entonces estás diciendo que debería usar '(length (next xs))' en la llamada recursiva, porque voy a llamar 'seq' en la colección de todos modos? En cuanto a 'coll?', En este punto solo me interesan los tipos de colección Clojure nativos, por lo que 'coll?' Debería hacerme muy bien. – liwp
De nada. Me refería sobre todo a llamar 'seq' al valor de retorno de' resto' directamente (por ejemplo '(if-let [new-xs (seq (rest xs))] ...)'), donde la expresión idiomática es definitivamente '(next xs) ', y' recur'ing con 'rest', que solo tiene sentido si no se puede llamar a' seq' en el valor de retorno en la siguiente iteración. En el caso de su función 'length', probablemente todavía use' next' para dejarlo lo más claro posible de que la función es estricta, pero diría que no hace mucha diferencia. –
Ok, ya veo, tiene sentido. – liwp