clojure symbols function

En Clojure, ¿cómo definir una variable nombrada por una cadena?



symbols function (4)

Dada una lista de nombres para variables, quiero establecer esas variables en una expresión.

Intenté esto:

(doall (for [x ["a" "b" "c"]] (def (symbol x) 666)))

... pero esto produce el error

java.lang.Exception: primer argumento para def debe ser un símbolo

¿Alguien puede mostrarme la forma correcta de lograr esto, por favor?


Las reglas de evaluación para llamadas a funciones normales son para evaluar todos los elementos de la lista y llamar al primer elemento de la lista como una función con el resto de los elementos de la lista como parámetros.

Pero no puede hacer suposiciones sobre las reglas de evaluación para formularios especiales o macros. Una forma especial o el código producido por una macro llamada podría evaluar todos los argumentos, o nunca evaluarlos, o evaluarlos varias veces, o evaluar algunos argumentos y no otros. def es una forma especial, y no evalúa su primer argumento. Si lo hiciera, no podría funcionar. La evaluación del foo en (def foo 123) daría como resultado un error "no var var foo" la mayoría de las veces (si foo ya estaba definido, probablemente no lo definirías tú mismo).

No estoy seguro para qué estás usando esto, pero no parece muy idiomático. El uso de la def cualquier lugar, pero a la altura de su programa, generalmente significa que está haciendo algo mal.

(Nota: doall + for = doseq )


(doall (for [x ["a" "b" "c"]] (eval `(def ~(symbol x) 666))))

En respuesta a tu comentario:

No hay macros involucradas aquí. eval es una función que toma una lista y devuelve el resultado de ejecutar esa lista como código. `y ~ son accesos directos para crear una lista parcialmente citada.

`significa que el contenido de las siguientes listas se debe citar a menos que esté precedido por un ~

~ la siguiente lista es una llamada a función que debe ejecutarse, no citarse.

Entonces `` (def ~ (symbol x) 666) is the list containing the symbol def , followed by the result of executing símbolo x followed by the number of the beast. I could as well have written followed by the number of the beast. I could as well have written (eval (list ''def (symbol x) 666)) `para lograr el mismo efecto.


La función "interna" de Clojure es para este propósito:

(doseq [x ["a" "b" "c"]] (intern *ns* (symbol x) 666))


Actualizado para tomar en cuenta el comentario de Stuart Sierra (mencionando clojure.core/intern ).

Usar eval aquí está bien, pero puede ser interesante saber que no es necesario, independientemente de si ya se sabe que los Vars ya existen. De hecho, si se sabe que existen, entonces creo que la solución alter-var-root continuación es más clara; si no existieran, entonces no insistiría en que mi proposición alternativa sea mucho más limpia, pero parece hacer el código más corto (si no tomamos en cuenta la sobrecarga de tres líneas para una definición de función), entonces solo publicaré para su consideración.

Si se sabe que el Var existe:

(alter-var-root (resolve (symbol "foo")) (constantly new-value))

Entonces podrías hacer

(dorun (map #(-> %1 symbol resolve (alter-var-root %2)) ["x" "y" "z"] [value-for-x value-for-y value-for z]))

(Si se utilizara el mismo valor para todos los Vars, podría usar (repeat value) para asignar el argumento final o simplemente ponerlo en la función anónima).

Si es necesario crear los Vars, puedes escribir una función para hacer esto (una vez más, no necesariamente afirmaría que es más limpio que eval , pero de todos modos, solo por el interés):

(defn create-var ;; I used clojure.lang.Var/intern in the original answer, ;; but as Stuart Sierra has pointed out in a comment, ;; a Clojure built-in is available to accomplish the same ;; thing ([sym] (intern *ns* sym)) ([sym val] (intern *ns* sym val)))

Tenga en cuenta que si un Var resulta haber sido ya internado con el nombre dado en el espacio de nombres dado, esto no cambia nada en el caso de un solo argumento o simplemente restablece el Var al nuevo valor dado en el caso de dos argumentos. Con esto, puedes resolver el problema original de la siguiente manera:

(dorun (map #(create-var (symbol %) 666) ["x" "y" "z"]))

Algunos ejemplos adicionales:

user> (create-var ''bar (fn [_] :bar)) #''user/bar user> (bar :foo) :bar user> (create-var ''baz) #''user/baz user> baz ; Evaluation aborted. ; java.lang.IllegalStateException: ; Var user/baz is unbound. ; It does exist, though! ;; if you really wanted to do things like this, you''d ;; actually use the clojure.contrib.with-ns/with-ns macro user> (binding [*ns* (the-ns ''quux)] (create-var ''foobar 5)) #''quux/foobar user> quux/foobar 5