2010-04-04 21 views
5

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.Ejecutando una función enlazada dinámicamente en Clojure

Esto funciona como estaba previsto para las funciones definidas a nivel de espacio de nombres con defn (a pesar de que la definición de función viene después de mi creación de la estructura de datos), pero no funcionará con funciones definidas por let [name (fn o letfn dentro de la función.

Aquí está mi pequeña autónomo ejemplo:

(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 usted se está preguntando acerca de mi configuración de prueba, para ver el resultado de esos 6 declaraciones de comentar/descomentar específica de permiso/líneas en su defecto y luego llame al (runit) desde REPL.

¿Hay una solución simple que podría llevar a cabo para obtener eval 'd quote d llamadas a funciones para trabajar con funciones definidas dentro de otra función?


Actualización:

Este (basado en la sugerencia de danlei) hace trabajo. 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! 
)) 

Actualización:

Este código va en mi solución para un Constraint Satisfaction Problem - Quiero averiguar who owns the zebra! 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.

que utiliza para 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 podría especificar el tipo de restricción. Pero eso me llevó a una aplicación incómoda de un mecanismo de expedición de dichas normas, así que decidí codificarlos como la función (citado) llama en su lugar:

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

por lo que puede enviar la regla de limitación con un 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 (llamados vars), 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 vars estuviera disponible para las funciones eval d mediante el alcance dinámico.

Ahora he aprendido que el alcance dinámico se puede hacer usando binding, pero también necesita un declare.

+1

¿Está simplemente a descansar los neumáticos o realmente tratando de poner en práctica algo? En este último caso, me gustaría saber qué estás tratando de hacer y eso te obligó a utilizar un diseño de este tipo: a primera vista, los retrasos o los viejos cierres normales podrían hacer el truco. – cgrand

+0

@cgrand: ¡Gracias por su interés! He agregado una segunda actualización para explicar lo que intento hacer. Esto mientras tanto trabaja para mí, pero estoy abierto a mejores sugerencias. –

+0

@cgrand: Mi publicación [Problema de satisfacción de restricciones] (http://stackoverflow.com/questions/2500504/constraint-satisfaction-problem) ahora tiene mi solución completa. Si está interesado, puede ver 'vinculante' en acción allí. Estoy muy abierto a la crítica constructiva en mi enfoque aficionado. –

Respuesta

5

¿Te refieres a algo como esto?

(def foo '(bar)) 
(declare bar) 

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

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

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

Esto no funcionará, porque eval no evalúa en el entorno léxico. Puede moverse por que el uso de Vars:

(declare foo) 

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

Por lo que se refiere, 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.

+0

Sí, parece que funciona. Es bueno que haya un número creciente de personas en SO que están familiarizadas con Clojure. ¡Gracias! –

+0

De nada. Supongo que esto es para fines educativos. De lo contrario, considere el comentario de cgrand a su pregunta. – danlei

3

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

intentar algo como esto, y el uso Assoc para cambiar el mapa local:

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 
+0

Gracias! Me complace confirmar que esto es exactamente lo que estoy haciendo. La codificación de mi solución de trabajo mientras tanto se puede encontrar en mi publicación [Problema de satisfacción de restricciones] (http://stackoverflow.com/questions/2500504/constraint-satisfaction-problem). –

Cuestiones relacionadas