2010-05-07 7 views
3

Je suis nouveau sur les langages fonctionnels et Clojure, donc s'il vous plaît garder avec moi ...Comment retourner la sortie d'une fonction récursive dans Clojure

Je suis en train de construire une liste de fonctions, soit au hasard paramètres ou constantes. La fonction qui construit la liste des fonctions fonctionne déjà, bien qu'elle ne renvoie pas la fonction elle-même. J'ai vérifié cela en utilisant println.

(modifier: D'accord, il ne fonctionne pas encore correctement après tout)

(edit: Maintenant, il fonctionne, mais il ne peut pas être « eval » il semble -ed que je dois reproduire au moins deux. fois, pour assurer il y a au moins deux noeuds enfants est-ce possible)

Voici l'extrait:.?

(def operations (list #(- %1 %2) #(+ %1 %2) #(* %1 %2) #(/ %1 %2))) 
(def parameters (list \u \v \w \x \y \z)) 
(def parameterlistcount 6) 
(def paramcount 2) 
(def opcount 4) 

(defn generate-function 


([] (generate-function 2 4 0.5 0.6() parameters)) 
    ([pc maxdepth fp pp function-list params] 
    (if (and (pos? maxdepth) (< (rand) fp)) 
     (let [function-list 
      (conj function-list 
        (nth operations 
         (rand-int (count operations))))] 
     (recur pc (dec maxdepth) fp pp function-list params)) 
     (if (and (< (rand) pp) (pos? pc)) 
     (let [ params (pop parameters) 
     function-list 
       (conj function-list 
         (nth params 
         (rand-int (count params))))] 
     (if (contains? (set operations) (last function-list)) 
      (recur (dec pc) maxdepth fp pp function-list params) 
      nil)) 
     (let [function-list 
       (conj function-list 
         (rand-int 100))] 
      (if (or (pos? maxdepth) (pos? pc)) 
      (if (contains? (set operations) (last function-list)) 
     (recur pc maxdepth fp pp function-list params) 
     nil) 
      function-list)))))) 

Toute aide sera appréciée, merci!

Répondre

2

En général, il est préférable d'utiliser Clojure (récurrence ...) pour vos fonctions récursives. De la docs: "Notez que recur est la seule construction en boucle non consommatrice de pile dans Clojure." Une autre chose à noter est que vous pouvez appeler le randomizer en dehors de la fonction récursive, vous pouvez donc définir la condition d'arrêt à l'intérieur de la fonction.

donc comme ceci:

(let [n (rand-int 10)] 
    (println "Let's define f with n =" n) 
    (defn f [x] 
    (if (> x n) 
     "result" 
     (do (println x) 
      (recur (inc x)))))) 

Il imprime:

Let's define f with n = 4 

user> (f 0) 
0 
1 
2 
3 
4 
"result" 

où 4 est bien sûr un nombre aléatoire entre 0 (inclus) et 10 (exclusive).

+0

Je suis reconnaissant pour les suggestions que vous m'avez données, mais je pense que j'aurais dû vous faire part de mon intention. Mon but est de créer une fonction, à utiliser comme population dans un problème de programmation génétique. @Michal - Je ne peux pas comprendre comment je pourrais (eval) ceci ... @Michiel - Je vois. – Silanglaya

3

Voici mon coup à réécrire votre fonction (voir les commentaires ci-dessous):

(defn generate-function 
    ([] (generate-function 2 4 0.5 0.6())) 
    ([pc maxdepth fp pp function-list] 
    (if (and (pos? maxdepth) (< (rand) fp)) 
     (let [function-list 
      (conj function-list 
        {:op 
        (nth operations 
         (rand-int (count operations)))})] 
     (recur pc (dec maxdepth) fp pp function-list)) 
     (if (and (< (rand) pp) (pos? pc)) 
     (let [function-list 
       (conj function-list 
        {:param 
         (nth parameters 
          (rand-int (count parameters)))})] 
      (recur (dec pc) maxdepth fp pp function-list)) 
     (let [function-list 
       (conj function-list 
        {:const 
         (rand-int 100)})] 
      (if (or (pos? maxdepth) (pos? pc)) 
      (recur pc maxdepth fp pp function-list) 
      function-list)))))) 

Et quelques exemples d'utilisation de mon REPL ...

user> (generate-function) 
({:const 63} {:op #<user$fn__4557 [email protected]>} {:const 77} {:param \w} {:op #<user$fn__4559 [email protected]>} {:const 3} {:param \v} {:const 1} {:const 8} {:op #<user$fn__4559 [email protected]>} {:op #<user$fn__4555 [email protected]>}) 
user> (generate-function) 
({:const 27} {:param \y} {:param \v} {:op #<user$fn__4561 [email protected]>} {:op #<user$fn__4561 [email protected]>} {:op #<user$fn__4561 [email protected]>} {:op #<user$fn__4561 [email protected]>} {:const 61}) 

Un couple de choses à garder à l'esprit , pour assez aléatoire:

  1. J'utilisé recur dans le ci-dessus pour éviter de consommer dans la pile les appels de soi récursifs. Cependant, vous avez cette déclaration dotimes qui me fait me demander si vous pourriez être intéressé par la construction d'un groupe de function-list s en parallèle avec un appel generate-function. Si oui, la récursivité avec recur peut ne pas être une option avec un code simpliste comme celui-ci, donc vous pouvez soit vous contenter des auto-appels réguliers (mais considérez la possibilité d'atteindre la limite de récursivité, si vous êtes positif) ll ne générera que des fonctions plus petites et ce ne sera pas un problème, allez-y avec les auto-appels) ou étudiez le style de continuation-passing et réécrivez votre fonction dans ce style.

  2. La chose (do (dec pc) ...) dans votre code ne fait rien à la valeur de pc dans l'appel récursif suivant, ou en effet à sa valeur actuelle. Les variables locales (ou locales, comme on les appelle le plus souvent dans la communauté) à Clojure sont immuables; ceci inclut les paramètres de la fonction.Si vous voulez passer un pc décrémenté à une fonction, vous devrez le faire, comme vous l'avez fait avec maxdepth dans une branche antérieure de votre code. J'ai renommé votre fonction en generate-function, car le nom de la fonction chameau dans les noms de fonctions est assez inhabituel dans les pays Clojure. En outre, j'ai renommé le paramètre que vous avez appelé function à function-list (donc peut-être que j'aurais dû utiliser un nom comme generate-function-list pour la fonction ... hm), parce que c'est ce que c'est pour l'instant.

  3. Notez qu'il n'y a pas de raison de garder un opcount Var autour; Les listes persistantes de Clojure (créées par la fonction list) portent leur nombre, donc (count some-list) est une opération à temps constant (et très rapide). En outre, il serait idiomatique d'utiliser des vecteurs pour operations et parameters (et vous pouvez passer à des vecteurs sans rien changer dans le reste du code!). Par exemple. [\u \v \w \x \y \z].

  4. Dans Clojure 1.2, vous pouvez utiliser (rand-nth coll) pour (nth coll (rand-int (count coll))).

  5. Si vous souhaitez générer des fonctions Clojure réelles à partir d'arborescences d'éléments représentant des opérations, des paramètres et des constantes, vous devez utiliser eval. C'est découragé dans la plupart des scénarios, mais pas pour la programmation évolutive et autres choses similaires où c'est la seule façon d'y aller.

  6. Personnellement, j'utiliser un format différent des op/PARAM/cartes constantes: quelque chose comme {:category foo, :content bar}foo est :op, :param ou :const et bar est quelque chose d'approprié dans le cadre d'une donnée foo.

1

Alors d'accord, j'ai découvert que j'allais à ce sujet dans le mauvais sens. Une définition récursive d'un arbre n'est autre que définir des sommets, et essayer de lier tout avec lui. Donc, je suis arrivé avec ça, en moins de 15 minutes. > _ <

(defn generate-branch 
"Generate branches for tree" 
    ([] (generate-branch 0.6() (list \x \y \z))) 
    ([pp branch params] 
     (loop [branch 
     (conj branch (nth operations (rand-int (count operations))))] 
    (if (= (count branch) 3) 
     branch 
     (if (and (< (rand) pp)) 
     (recur (conj branch (nth params (rand-int (count params))))) 
     (recur (conj branch (rand-int 100)))))))) 

(defn build-vertex 
"Generates a vertex from branches" 
    [] 
    (let [vertex (list (nth operations (rand-int (count operations))))] 
    (conj vertex (take 5 (repeatedly generate-branch))))) 

MERCI TOUT LE MONDE!