recursion clojure functional-programming lisp

recursion - ¿La mejor forma de acumular resultados en un vector en Clojure?(El código funcional puro parece feo y detallado)



functional-programming lisp (5)

En orden descendente de lo bien que creo que se ven las opciones:

(for [x coll, y (nested-collection x) :when (not (some-condition-holds y))] y)

Alternativamente, si prefiere construirlo fuera de funciones como map y mapcat lugar de usar la sintaxis for :

(mapcat (fn [x] (remove some-condition-holds (nested-collection x))) coll)

Si está realmente interesado en él, también puede compilarlo con aplicaciones de funciones parciales y composición:

(mapcat (comp (partial remove some-condition-holds) nested-collection) coll)

Este tercer estilo no se lee muy bien en Clojure, aunque el código equivalente en algunos otros idiomas es muy bueno. En Haskell, por ejemplo:

coll >>= (filter (not . someConditionHolds) . nestedCollection)

... Tal vez la programación imperativa con datos variables se haya perforado demasiado en mi cerebro, pero creo que el código para construir vectores de datos en Clojure es prolijo, difícil de manejar y complicado. ¡Debe haber una mejor manera!

En Ruby podría escribir código como:

results = [] a_collection.each do |x| x.nested_collection.each do |y| next if some_condition_holds results << y end end

En Clojure, no sé de una mejor manera de hacer eso que usar una función recursiva, tal como el siguiente código (horrendo):

; NEWBIE ALERT! NEWBIE ALERT! (loop [results [] remaining a_collection] (if (empty? remaining) results (recur (loop [results results nested (nested_collection (first remaining))] (if (empty? nested) results (if (some_condition_holds) (recur results (rest nested)) (recur (conj results (first nested)) (rest nested))))) (rest remaining))))

Sin datos mutables y bucles iterativos, debe usar la recursión para crear una colección. Cada función recursiva de este tipo necesita una cláusula de guardia (empty?) , Etc. Todo el asunto es tan repetitivo que me dan ganas de gritar.

En casos simples, el map sería suficiente, pero estoy pensando en casos en los que hay múltiples niveles de anidación, y en cada nivel, puede haber condiciones que requieren omitir una iteración.

En Common Lisp, podría usar la macro loop , o mapcan . ¿Clojure no tiene nada parecido a mapcan ?


Otros ya han proporcionado respuestas sobre cómo resolver el problema especificado utilizando conceptos de FP como el uso de funciones de orden superior. Si analiza su proceso de pensamiento que conduce a su código actual y lo compara con las soluciones de PF que otras personas le han proporcionado, encontrará que cada vez que piense en "tener una variable para almacenar el resultado procesado", se traducirá en un imperativo O paso a paso, tipo de solución y, por lo tanto, su código Clojure es, en su mayoría, imprescindible ya que pensó en almacenar el resultado como una "variable de vector". Este tipo de pensamiento no le permitirá aplicar conceptos de PF basados ​​en la "evaluación de la expresión" y la "solución del problema por composición".


las funciones de orden superior realmente pueden ayudar a que sea mucho más bella, aunque lleva un tiempo acostumbrarse a pensar en secuencias y transformación de secuencias.

hay muchas formas de escribir esto:

user> (into [] a_colletion) [0 1 2 3 4 5 6 7 8 9] user> (vec a_colletion) [0 1 2 3 4 5 6 7 8 9] user> (for [x a_colletion :when (even? x)] x) (0 2 4 6 8)

un ejemplo más complejo podría verse más o menos así:

(flatten (for [x (map extract-from-nested-collection a_collection) :when (test-conditions? x)] x))

hacer una colección anidada

user> (def a_collection (map #(reductions + (range %)) (range 1 5))) #''user/a_collection user> a_collection ((0) (0 1) (0 1 3) (0 1 3 6))

recupera una colección anidada de cada elemento de a_collection y salta algunos de ellos:

user> (map #(filter pos? %) a_collection) (() (1) (1 3) (1 3 6))

anexar las colecciones anidadas

user> (flatten (map #(filter pos? %) a_collection)) (1 1 3 1 3 6)

filtre algo más grande que 3 de la colección aplanada y luego cuadre cada uno de ellos

user> (for [x (flatten (map #(filter pos? %) a_collection)) :when (<= x 3)] (* x x)) (1 1 9 1 9) user>


La respuesta de amalloy es probablemente la mejor si se quiere seguir un estilo funcional idiomático y producir una secuencia perezosa.

Si en realidad está interesado en construir imperativamente un vector (en lugar de una secuencia perezosa), probablemente lo haría usando un átomo y una dosisq de la siguiente manera:

(let [v (atom [])] (doseq [x (range 5) y (range 5)] (if (> y x) (swap! v conj (str x y)))) @v) => ["01" "02" "03" "04" "12" "13" "14" "23" "24" "34"]

Como puede ver, esto termina muy similar en estructura al código de Ruby.

También es posible hacer esto usando reducir, sin embargo, esto es más adecuado cuando solo hay una secuencia de entrada, por ejemplo:

(reduce (fn [v x] (if (even? x) (conj v x) v)) [] (range 20)) => [0 2 4 6 8 10 12 14 16 18]


(mapcat (fn [y] (filter condition y)) x)