Incluso una vez que el átomo se establece en true
, su versión no puede dejar de correr hasta que los interiores acabados doseq
(hasta y> x). Sin embargo, terminará el bucle externo una vez que el bucle interno termine. Termina eventualmente cuando lo ejecuto. No estoy seguro de lo que estás viendo
No necesita dos doseq
s para hacer esto. Uno doseq
puede manejar dos seqs a la vez.
user> (doseq [x (range 0 2) y (range 3 6)] (prn [x y]))
[0 3]
[0 4]
[0 5]
[1 3]
[1 4]
[1 5]
(Lo mismo es cierto de for
.) No existe un mecanismo para "romper" de doseqs anidados, que yo sepa, excepto throw
/catch
, pero eso es bastante ONU-idiomática. No necesita átomos o doseq
para esto.
(def answers (filter (fn [[x y]] (both-pent? x y))
(for [x pentagonal-list
y pentagonal-list :while (<= y x)]
[x y])))
Su código es muy imperativo en estilo. "Pasa el cursor por encima de estas listas, luego prueba los valores, luego imprime algo y luego deja de dar vueltas". Usar átomos para un control como este no es muy idiomático en Clojure.
Una forma más funcional es tomar un seq (lista pentagonal) y envolverlo en funciones que lo convierten en otros seqs hasta que obtenga un seq que le proporcione lo que desea. Primero uso for
para convertir dos copias de estos seqs en un seq de pares donde y < = x. Luego uso filter
para convertir ese seq en uno que filtra los valores que no nos importan.
filter
y for
son flojos, por lo que esto dejará de funcionar una vez que encuentre el valor válido de first
, si es que uno es todo lo que desea. Esto devuelve los dos números que desea, y luego puede restarlos.
(apply - (first answers))
O puede envolver aún más la función en otro map
para calcular las diferencias para ti.
(def answers2 (map #(apply - %) answers))
(first answers2)
La programación funcional de esta manera presenta algunas ventajas. El seq se almacena en caché (si se sostiene sobre la cabeza como lo hago aquí), de modo que una vez que se calcula un valor, lo recuerda y puede acceder a él instantáneamente a partir de ese momento. Su versión no se puede ejecutar de nuevo sin reiniciar el átomo, y luego tendría que volver a calcular todo. Con mi versión puede (take 5 answers)
obtener los primeros 5 resultados, o mapear el resultado para hacer otras cosas si lo desea. Puede doseq
sobre él e imprimir los valores. Etc. etc.
Estoy seguro de que hay otras formas (quizás mejores) de hacerlo sin utilizar un átomo. Por lo general, debe evitar las referencias de mutación a menos que sea 100% necesario en Clojure.
Los nombres de función para alterar átomos/agentes/refs son diferentes, probablemente porque las mecánicas no son las mismas. Las referencias son sincrónicas y coordinadas a través de transacciones. Los agentes son asincrónicos. Los átomos son sincrónicos y descoordinados. Todos ellos tipo de "cambiar una referencia", y, probablemente, algún tipo de súper función o macro podría envolver a todos en un solo nombre, pero que oscurecería el hecho de que están haciendo cosas drásticamente diferentes bajo el capó. Explicar las diferencias por completo probablemente esté más allá del alcance de una publicación SO para explicar, pero http://clojure.org explica completamente todos los matices de las diferencias.
Gracias. No me di cuenta de que puedo hacer el "bucle" en una sola dosisq/por. El truco con usar primero en la lista pentagonal filtrada y desestructurada es genial. – fizbin