Bei Verwendung von reduce
gibt es die reductions
Funktion, um die Liste der aufeinanderfolgenden Reduzierungen anzuzeigen. Gibt es etwas Ähnliches Debug loop
in Clojure?clojure loop siehe Werte
Antwort
@progo ist richtig, Sie können immer einen weiteren Akkumulator hinzufügen, aber wenn Sie es nicht wirklich jedes Mal tun möchten, könnten Sie einige Makros machen, die für Sie semantisch dem Standard loop/recur
entsprechen (ich denke an zwei loop+/recur+
(letztere implizit verwendet werden würde):
(defmacro recur+ [& args]
(let [names (repeatedly (count args) gensym)]
`(let ~(vec (interleave names args))
(recur [email protected] (conj ~'&loop-history [[email protected]])))))
(defmacro loop+ [bindings & body]
(let [val-names (repeatedly (/ (count bindings) 2) gensym)
vals (take-nth 2 (rest bindings))
binding-lefts (take-nth 2 bindings)]
`(let [[email protected](interleave val-names vals)]
(loop [[email protected](interleave binding-lefts val-names)
~'&loop-history [~(vec val-names)]]
[email protected](clojure.walk/postwalk-replace
{'recur 'recur+
'loop 'loop+}
body)))))
wie Sie sehen können, loop+
führt den impliziten Wert &loop-history
und ersetzt alle inneren loop
s und recur
s mit loop+
und recur+
, während recur+
fügt diese implizite var derhinzuAnruf (der Teil mit val-names
, vals
und binging-lefts
ist wesentlich, um die doppelte Bewertung der Formulare zu vermeiden, die an die loop+
übergeben werden).
so, stellen Sie sich eine Schleife wie dieses:
user> (loop [a 1 b 2]
(if (<= b 10)
(recur a (inc b))
(str a " " b)))
"1 11"
die neue Schleife verwenden nur Schleife aufrufen + statt Schleife:
user> (loop+ [a 1 b 2]
(if (<= b 10)
(recur a (inc b))
(str a " " b)))
"1 11"
es in den folgenden erweitert wird:
(let*
[G__20054 1 G__20055 2]
(loop*
[a G__20054 b G__20055 &loop-history [[G__20054 G__20055]]]
(if (<= b 10)
(let*
[G__20056 a G__20057 (inc b)]
(recur
G__20056
G__20057
(conj &loop-history [G__20056 G__20057])))
(str a " " b))))
jetzt &loop-history
ist überall zugänglich innerhalb der Schleife +:
user> (loop+ [a 1 b 2]
(if (<= b 10)
(do
(println "history length: " (count &loop-history)
"last item: " (last &loop-history))
(recur a (inc b)))
{:result (str a " " b)
:history &loop-history}))
;; history length: 1 last item: [1 2]
;; history length: 2 last item: [1 3]
;; history length: 3 last item: [1 4]
;; history length: 4 last item: [1 5]
;; history length: 5 last item: [1 6]
;; history length: 6 last item: [1 7]
;; history length: 7 last item: [1 8]
;; history length: 8 last item: [1 9]
;; history length: 9 last item: [1 10]
;; {:result "1 11", :history [[1 2] [1 3] [1 4] [1 5] [1 6] [1 7] [1 8] [1 9] [1 10] [1 11]]}
Ankündigung, dass es führt auch &loop-history
für innere Schleifen, ohne die Notwendigkeit, den Quellcode zu ändern:
user> (loop+ [a 1 b 2]
(if (<= b 10)
(do (println :outer-hist &loop-history)
(recur a (inc b)))
(loop [a a]
(if (>= a -4)
(do (println :inner-hist &loop-history)
(recur (dec a)))
(str a b)))))
:outer-hist [[1 2]]
:outer-hist [[1 2] [1 3]]
:outer-hist [[1 2] [1 3] [1 4]]
:outer-hist [[1 2] [1 3] [1 4] [1 5]]
:outer-hist [[1 2] [1 3] [1 4] [1 5] [1 6]]
:outer-hist [[1 2] [1 3] [1 4] [1 5] [1 6] [1 7]]
:outer-hist [[1 2] [1 3] [1 4] [1 5] [1 6] [1 7] [1 8]]
:outer-hist [[1 2] [1 3] [1 4] [1 5] [1 6] [1 7] [1 8] [1 9]]
:outer-hist [[1 2] [1 3] [1 4] [1 5] [1 6] [1 7] [1 8] [1 9] [1 10]]
:inner-hist [[1]]
:inner-hist [[1] [0]]
:inner-hist [[1] [0] [-1]]
:inner-hist [[1] [0] [-1] [-2]]
:inner-hist [[1] [0] [-1] [-2] [-3]]
:inner-hist [[1] [0] [-1] [-2] [-3] [-4]]
"-511"
loop
/recur
ist zwingend erforderlich als reductions
; Sie können sehr gut nur Ihre eigene Protokollierung in den Schleifenkörper selbst einfügen oder eine Atomliste von Werten beibehalten oder, am funktionalsten, einen (anderen) Akkumulator in die Schleifenargumente einführen.
Hat es eine faule Sequenz zurückkehren wie 'reductions' tut? –