3 Stimmen

Wie man die Ausgabe einer rekursiven Funktion in Clojure zurückgibt

Ich bin neu in funktionalen Sprachen und Clojure, so bitte mit mir zu tragen...

Ich versuche, eine Liste von Funktionen zu erstellen, entweder mit zufälligen Parametern oder Konstanten. Die Funktion, die die Liste der Funktionen konstruiert, funktioniert bereits, obwohl sie die Funktion selbst nicht zurückgibt. Ich habe dies mit println überprüft.

(edit: Okay, es funktioniert also doch noch nicht richtig)

(edit: Jetzt funktioniert es, aber es kann nicht "eval"-ed werden. Es scheint, dass ich mindestens zwei Mal wiederholen muss, um sicherzustellen, dass es mindestens zwei Kinderknoten gibt. Ist das möglich?)

Hier ist der Ausschnitt:

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

Für jede Hilfe sind wir dankbar, danke!

3voto

Michał Marczyk Punkte 82196

Hier ist mein Versuch, Ihre Funktion umzuschreiben (siehe Kommentare unten):

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

Und einige Anwendungsbeispiele aus meiner REPL...

user> (generate-function)
({:const 63} {:op #<user$fn__4557 user$fn__4557@6cbb2d>} {:const 77} {:param \w} {:op #<user$fn__4559 user$fn__4559@8e68bd>} {:const 3} {:param \v} {:const 1} {:const 8} {:op #<user$fn__4559 user$fn__4559@8e68bd>} {:op #<user$fn__4555 user$fn__4555@6f0962>})
user> (generate-function)
({:const 27} {:param \y} {:param \v} {:op #<user$fn__4561 user$fn__4561@10463c3>} {:op #<user$fn__4561 user$fn__4561@10463c3>} {:op #<user$fn__4561 user$fn__4561@10463c3>} {:op #<user$fn__4561 user$fn__4561@10463c3>} {:const 61})

Ein paar Dinge, die man beachten sollte, in ziemlich zufälliger Reihenfolge:

  1. Ich habe recur um zu vermeiden, dass bei den rekursiven Selbstaufrufen Stack verbraucht wird. Sie haben jedoch diese dotimes Anweisung, weshalb ich mich frage, ob Sie vielleicht daran interessiert wären, eine Reihe von function-list s parallel zu einem generate-function anrufen. Ist dies der Fall, wird die Rekursion mit recur ist bei einfachem Code wie diesem vielleicht keine Option, also könnten Sie sich entweder mit den regulären Selbstaufrufen begnügen (aber bedenken Sie die Möglichkeit, auf die Rekursionsgrenze zu stoßen; wenn Sie sicher sind, dass Sie nur kleine Funktionen erzeugen und dies kein Problem sein wird, fahren Sie mit den Selbstaufrufen fort) oder Sie untersuchen den Continuation-Passing-Stil und schreiben Ihre Funktion in diesem Stil um.

  2. Die (do (dec pc) ...) in Ihrem Code hat keinen Einfluss auf den Wert von pc beim nächsten rekursiven Aufruf oder auf seinen aktuellen Wert. Lokale Variablen (oder Locals, wie sie in der Community meist genannt werden) sind in Clojure unveränderlich; das gilt auch für Funktionsparameter. Wenn Sie eine dekrementierte Variable weitergeben wollen pc zu einer Funktion zu machen, müssen Sie genau das tun, wie Sie es mit maxdepth in einem früheren Zweig Ihres Codes.

  3. Ich habe Ihre Funktion umbenannt in generate-function denn Groß- und Kleinschreibung in Funktionsnamen ist in Clojure recht ungewöhnlich. Außerdem habe ich den Parameter umbenannt, den Sie function a function-list (vielleicht hätte ich also einen Namen verwenden sollen wie generate-function-list für die Funktion... hm), denn das ist es, was es jetzt ist.

  4. Beachten Sie, dass es keinen Sinn macht, eine separate opcount Var herum; die persistenten Listen von Clojure (wie sie von der list Funktion) tragen ihren Zählerstand mit sich herum, so dass (count some-list) ist ein Vorgang mit konstanter Zeit (und sehr schnell). Außerdem wäre es idiomatisch, Vektoren zu verwenden für operations y parameters (und Sie können zu Vektoren wechseln, ohne etwas am restlichen Code zu ändern!). z.B.. [\u \v \w \x \y \z] .

  5. In Clojure 1.2 werden Sie in der Lage sein, die (rand-nth coll) para (nth coll (rand-int (count coll))) .

  6. Wenn Sie tatsächliche Clojure-Funktionen aus Bäumen von Elementen generieren wollen, die Ops, Params und Konstanten repräsentieren, werden Sie die eval . Davon wird in den meisten Szenarien abgeraten, aber nicht bei der evolutionären Programmierung und ähnlichen Dingen, wo es der einzige Weg ist.

  7. Ich persönlich würde ein anderes Format für die op/param/constant-Maps verwenden: etwa so {:category foo, :content bar} donde foo es :op , :param o :const y bar ist etwas, das im Zusammenhang mit einer bestimmten foo .

2voto

Michiel Borkent Punkte 32952

Im Allgemeinen ist es in Clojure eine bessere Idee, (recur ...) für rekursive Funktionen zu verwenden. Aus der Doku: "Beachten Sie, dass recur das einzige nicht stackverbrauchende Schleifenkonstrukt in Clojure ist." lien

Außerdem sollten Sie den Randomizer außerhalb der rekursiven Funktion aufrufen, damit Sie die Stopp-Bedingung innerhalb der Funktion definieren können.

Also etwa so:

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

Er druckt:

Let's define f with n = 4

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

wobei 4 natürlich eine Zufallszahl zwischen 0 (einschließlich) und 10 (ausschließlich) ist.

1voto

Silanglaya Punkte 93

Also gut, ich habe festgestellt, dass ich die Sache falsch angegangen bin. Eine rekursive Definition eines Baumes ist nichts anderes als die Definition von Eckpunkten und der Versuch, alles damit zu verbinden. Also habe ich mir das hier ausgedacht, in weniger als 15 Minuten. >_<

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

DANKE AN ALLE!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X