2010-04-04 5 views
5

Je voudrais appels pré-enregistrer un groupe de fonction dans une structure de données, puis évaluer/les exécuter à partir d'une autre fonction.L'exécution d'une fonction dynamique lié à Clojure

Cela fonctionne comme prévu pour les fonctions définies au niveau de l'espace de noms avec defn (même si la définition de la fonction vient après ma création de la structure de données), mais ne fonctionnera pas avec les fonctions définies par let [name (fn ou letfn dans la fonction.

Voici mon petit exemple autonome:

(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 
))) 

Si vous vous interrogez sur ma configuration de test, pour voir le résultat de ces 6 déclarations I commentaire/spécifique uncomment du OK/lignes défaillantes et puis appelez (runit) depuis le REPL.

Y at-il une solution simple que je pourrais entreprendre pour obtenir eval 'd quote d appels à des fonctions pour travailler pour des fonctions définies dans une autre fonction?


Mise à jour:

Ce (basé sur la suggestion de Danlei) -t travail. Voyons voir si je peux obtenir cette méthode de travail dans la «vraie vie!

(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! 
)) 

Mise à jour:

Ce code va dans ma solution pour une Constraint Satisfaction Problem - Je veux savoir who owns the zebra! Je suis assez nouveau à Clojure et surtout à la programmation fonctionnelle, et cela a rendu l'exercice assez difficile. Je tombe dans beaucoup de puits, mais je suis d'accord avec cela car cela fait partie de l'expérience d'apprentissage.

J'utilisé pour spécifier les contraintes comme un tas de vecteurs simples, comme ceci:

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

où le premier de chaque vecteur spécifier le type de contrainte. Mais cela m'a conduit à une mise en œuvre maladroite d'un mécanisme de répartition de ces règles, alors j'ai décidé de les coder en fonction (entre guillemets) appelle à la place:

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

donc je peux envoyer la règle contraignante avec un eval simple. Cela semble beaucoup plus élégant et "lisp-y". Cependant, chacune de ces fonctions doit accéder à mes données de domaine (nommées vars) et ces données changent constamment au cours de l'exécution du programme. Je ne voulais pas ternir mes règles en introduisant un argument supplémentaire, donc je voulais que vars soit disponible pour les fonctions eval 'd via la portée dynamique.

J'ai maintenant appris que la portée dynamique peut être faite en utilisant binding, mais il faut aussi declare.

+1

Êtes-vous simplement avoir les pneus ou vraiment essayer de mettre en œuvre quelque chose? Si c'est le cas, j'aimerais savoir ce que vous essayez de faire qui vous a forcé à utiliser un tel design - à première vue, des retards ou de simples fermetures pourraient faire l'affaire. – cgrand

+0

@cgrand: Merci pour votre intérêt! J'ai ajouté une deuxième mise à jour pour expliquer ce que j'essaie de faire. Cela travaille en ce moment pour moi mais je suis ouvert à de meilleures suggestions! –

+0

@cgrand: Mon message [Problème de satisfaction de contrainte] (http://stackoverflow.com/questions/2500504/constraint-satisfaction-problem) a maintenant ma solution complète. Si vous êtes intéressé, vous pouvez voir "binding" en action là-bas. Je suis très ouvert à la critique constructive sur mon approche amateur. –

Répondre

5

Voulez-vous dire quelque chose comme ça?

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

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

Si oui, votre problème se réduit à ceci:

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

Cela ne fonctionnera pas, parce que eval n'évalue pas dans l'environnement lexical. Vous pouvez contourner qu'utiliser vars:

(declare foo) 

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

En ce qui concerne ce qui est, Clojure semble avoir une sémantique similaire à CL, cf. le CLHS:

Évalue la forme dans l'environnement dynamique actuel et l'environnement lexical nul.

+0

Oui, semble fonctionner. C'est bien qu'il y ait de plus en plus de gens qui parlent couramment le Clojure. Merci! –

+0

De rien. Je suppose que c'est à des fins éducatives. Sinon, considérez le commentaire de cgrand à votre question. – danlei

3

Je pense que vous résolvez le mauvais problème. Dans les langages fonctionnels, les fonctions sont des valeurs et peuvent être affectées à tout ce qui peut stocker n'importe quelle autre valeur, par ex. une carte. Vous ne devriez pas essayer de manipuler des espaces de noms ou d'évaluer quoi que ce soit - ce n'est pas perl.

Essayez quelque chose comme ça, et l'utilisation assoc changer la carte locale:

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

Merci! Je suis heureux de confirmer que c'est exactement ce que je fais. Le codage de ma solution de travail en attendant peut être trouvé dans mon message [Problème de satisfaction de contrainte] (http://stackoverflow.com/questions/2500504/contraint-satisfaction-problem). –