binding clojure eval

binding - Ejecutando una función enlazada dinámicamente en Clojure



eval (2)

Me gustaría pre-almacenar un montón de llamadas a funciones en una estructura de datos y luego evaluarlas / ejecutarlas desde otra función.

Esto funciona según lo planeado para las funciones definidas en el nivel de espacio de nombres con defn (aunque la definición de función viene después de mi creación de la estructura de datos) pero no funciona con las funciones definidas por let [name (fn o letfn dentro de la función.

Aquí está mi pequeño ejemplo autocontenido:

(def todoA ''(funcA)) (def todoB ''(funcB)) (def todoC ''(funcC)) (def todoD ''(funcD)) ; unused (defn funcA [] (println "hello funcA!")) (declare funcB funcC) (defn runit [] (let [funcB (fn [] (println "hello funcB"))] (letfn [(funcC [] (println "hello funcC!"))] (funcA) ; OK (eval todoA) ; OK (funcB) ; OK (eval todoB) ; "Unable to resolve symbol: funcB in this context" at line 2 (funcC) ; OK (eval todoC) ; "Unable to resolve symbol: funcC in this context" at line 3 )))

En caso de que se esté preguntando acerca de la configuración de mi prueba, para ver el resultado de esas 6 afirmaciones, comente / elimine comentarios específicos de las líneas (runit) y luego llame (runit) desde REPL.

¿Existe una solución simple que podría llevarme a cabo para eval las llamadas a las funciones para que funcionen para funciones definidas dentro de otra función?

Actualizar:

Esto (basado en la sugerencia de danlei) funciona. Veamos si puedo lograr que este método funcione en "la vida real".

(def todoB ''(funcB)) (declare funcB) (defn runit [] (binding [funcB (fn [] (println "hello funcB"))] (funcB) (eval todoB) ; "Unable to resolve symbol: funcB in this context" at line 1! ))

Actualizar:

Este código va a mi solución para un Problema de Satisfacción de Restricción - ¡Quiero saber a quién pertenece la cebra ! Soy bastante nuevo en Clojure y especialmente en la programación funcional, y esto ha hecho que el ejercicio sea bastante desafiante. Estoy cayendo en muchos pozos, pero estoy de acuerdo con eso, ya que es parte de la experiencia de aprendizaje.

Solía ​​especificar las restricciones como un grupo de vectores simples, como este:

[:con-eq :spain :dog] [:abs-pos :norway 1] [:con-eq :kools :yellow] [:next-to :chesterfields :fox]

donde el primero de cada vector especificaría el tipo de restricción. Pero eso me llevó a una implementación incómoda de un mecanismo de envío para esas reglas, así que decidí codificarlas como llamadas de funciones (cotizadas) en su lugar:

''(coloc :japan :parliament) ; 10 ''(coloc :coffee :green) ; 12 ''(next-to :chesterfield :fox) ; 5

entonces puedo despachar la regla restrictiva con una simple eval . Esto parece mucho más elegante y "lisp-y". Sin embargo, cada una de estas funciones necesita acceder a mis datos de dominio ( vars nombrados), y estos datos cambian constantemente a medida que se ejecuta el programa. No quería manchar mis reglas presentando un argumento adicional, así que quería que los vars estuvieran disponibles para las funciones de eval través del alcance dinámico.

Ahora aprendí que el alcance dinámico se puede hacer mediante el binding , pero también necesita una declare .


¿Te refieres a algo como esto?

(def foo ''(bar)) (declare bar) (binding [bar (fn [] (println "hello bar"))] (eval foo))

Si es así, tu problema se reduce a esto:

(let [foo 1] (eval ''foo))

Esto no funcionará, porque eval no evalúa en el entorno léxico. Puedes moverte usando vars:

(declare foo) (binding [foo 1] (eval ''foo))

En cuanto a eso, Clojure parece tener una semántica similar a CL, cf. el CLHS :

Evalúa la forma en el entorno dinámico actual y el entorno léxico nulo.


Creo que estás resolviendo el problema equivocado. En los lenguajes funcionales, las funciones son valores y pueden asignarse a cualquier cosa que pueda almacenar cualquier otro valor, por ejemplo, un mapa. No deberías tratar de manipular espacios de nombres o evaluar nada, esto no es perl.

Pruebe algo como esto, y use assoc para cambiar el mapa localmente:

user=> (def fnmap {:funcA (fn [x] (inc x)), :funcB (fn [x] (* x 2))}) #''user/fnmap user=> ((:funcA fnmap) 10) 11 user=> ((:funcB fnmap) 10) 20