Pregunta

OK. He estado jugando con Clojure y yo continuamente topado con el mismo problema. Vamos a tomar este pequeño fragmento de código:

(let [x 128]
  (while (> x 1)
    (do
      (println x)
      (def x (/ x 2)))))

Ahora me esperaba esto para imprimir una secuencia que comienza con 128, como así:

128
64
32
16
8
4
2

En cambio, es un bucle infinito, la impresión de 128 y otra vez. Está claro que mi intención efecto secundario no está funcionando.

Entonces, ¿cómo se supone que voy a redefinir el valor de x en un circuito como este? Me doy cuenta de que esto no puede ser igual que Lisp (que podría utilizar una función anónima que recursivamente en él mismo, tal vez), pero si no averiguar cómo establecer la variable de este tipo, voy a ir loco.

Mi otra conjetura sería el uso de establecer !, pero eso da "destino de la asignación no válido", ya que no estoy en una forma de unión.

Por favor, me ilumine sobre cómo se supone que funciona.

¿Fue útil?

Solución

def define un nivel superior var, incluso si se utiliza en una función o un bucle interno de un cierto código. Lo que se obtiene en let no son vars. Por la documentación para let :

  

Los locales creados con let no son variables. Una vez creado sus valores nunca cambian!

No es necesario el estado mutable por su ejemplo aquí (El subrayado no es mío.); usted podría utilizar loop y recur.

(loop [x 128]
  (when (> x 1)
    (println x)
    (recur (/ x 2))))

Si usted quiere ser de lujo podría evitar la loop explícita por completo.

(let [xs (take-while #(> % 1) (iterate #(/ % 2) 128))]
  (doseq [x xs] (println x)))

Si realmente querían utilizar el estado mutable, un átomo de podría funcionar .

(let [x (atom 128)]
  (while (> @x 1)
    (println @x)
    (swap! x #(/ %1 2))))

(No es necesario un do; while envuelve su cuerpo en una explícita para usted.) Si realmente quería hacer esto con vars que tendría que hacer algo horrible como éste.

(with-local-vars [x 128]
  (while (> (var-get x) 1)
    (println (var-get x))
    (var-set x (/ (var-get x) 2))))

Pero esto es muy feo y no es Clojure idiomática en absoluto. Para utilizar con eficacia Clojure usted debe tratar de dejar de pensar en términos de estado mutable. Definitivamente le vuelven loco tratando de escribir código Clojure en un estilo no-funcional. Después de un tiempo puede que le resulte ser una agradable sorpresa lo poco que realmente necesitan las variables mutables.

Otros consejos

Vars (eso es lo que se obtiene cuando se "def" algo) no están destinados a ser reasignado (pero puede ser):

user=> (def k 1)
#'user/k
user=> k
1

No hay nada que nos impida hacerlo:

user=> (def k 2)
#'user/k
user=> k
2

Si quieres un ajustable "lugar" local de subprocesos puede utilizar "unión" y "set":

user=> (def j) ; this var is still unbound (no value)
#'user/j
user=> j
java.lang.IllegalStateException: Var user/j is unbound. (NO_SOURCE_FILE:0)
user=> (binding [j 0] j)
0

Entonces se puede escribir un bucle de la siguiente manera:

user=> (binding [j 0]
         (while (< j 10)
           (println j)
           (set! j (inc j))))
0
1
2
3
4
5
6
7
8
9
nil

Pero creo que esto es bastante unidiomatic.

Si usted piensa que tiene variables locales mutables en funciones puras sería una herramienta de gran utilidad agradable que no hace ningún daño, ya que la función sigue siendo pura, que podría estar interesado en esta lista de correo de discusión donde Rich Hickey explica sus razones para la eliminación de ellos de la lengua. por qué no locales mutables?

Parte pertinente:

  

Si los locales fueron variables, es decir, mutable, a continuación, los cierres podría cerrar por encima   estado mutable, y, dado que los cierres pueden escapar (sin un poco de sobrepeso   prohibición en el mismo), el resultado sería hilo-inseguro. y las personas   sin duda hacerlo, por ejemplo, a base de cierre pseudo-objetos. El resultado   sería un enorme agujero en el enfoque de Clojure.

     

Sin mutables locales, las personas se ven obligadas a utilizar se repiten, una funcional   bucle constructo. Si bien esto puede parecer extraño al principio, es tan   sucinta como bucles con la mutación, y los patrones resultantes pueden ser   reutilizado en Clojure en otro lugar, es decir, vuelve a producirse, reducir, alterar, conmutar etc   son todos (lógicamente) muy similar. A pesar de que yo pudiera detectar y   prevenir la mutación de los cierres de escape, he decidido mantener de esta manera   por consistencia. Incluso en el contexto más pequeño, bucles no están mutando   más fácil de entender y de depuración de los mutando. En cualquier caso, Vars   están disponibles para su uso cuando sea apropiado.

La mayoría de los mensajes subsiguientes preocupaciones de ejecución una macro with-local-vars;)

Se puede usar más idiomáticamente iterate y take-while lugar,

user> (->> 128
           (iterate #(/ % 2))
           (take-while (partial < 1)))

(128 64 32 16 8 4 2)
user>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top