Evaluates the exprs in a lexical context in which the symbols in
the binding-forms are bound to their respective init-exprs or parts
therein. Acts as a recur target.
;looping is recursive in Clojure, the loop construct is a hack so that something like tail-recursive-optimization works in clojure.
user=> (defn my-re-seq [re string]
"Something like re-seq"
(let [matcher (re-matcher re string)]
(loop [match (re-find matcher) ;loop starts with 2 set arguments
result []]
(if-not match
result
(recur (re-find matcher) ;loop with 2 new arguments
(conj result match))))))
#'user/my-re-seq
user=> (my-re-seq #"\d" "0123456789")
["0" "1" "2" "3" "4" "5" "6" "7" "8" "9"]
;; Read decoded MP3 data in loop (requires mp3plugin.jar on class path)
;; http://java.sun.com/javase/technologies/desktop/media/jmf/mp3/download.html
(import '(javax.sound.sampled AudioSystem AudioFormat$Encoding))
(let [mp3-file (java.io.File. "tryout.mp3")
audio-in (AudioSystem/getAudioInputStream mp3-file)
audio-decoded-in (AudioSystem/getAudioInputStream AudioFormat$Encoding/PCM_SIGNED audio-in)
buffer (make-array Byte/TYPE 1024)]
(loop []
(let [size (.read audio-decoded-in buffer)]
(when (> size 0)
;do something with PCM data
(recur)))))
(defn find-needle [needle haystack]
;loop binds initial values once,
;then binds values from each recursion call
(loop [needle needle
maybe-here haystack
not-here '()]
(let [needle? (first maybe-here)]
;test for return or recur
(if (or (= (str needle?) (str needle))
(empty? maybe-here))
;return results
[needle? maybe-here not-here]
;recur calls loop with new values
(recur needle
(rest maybe-here)
(concat not-here (list (first maybe-here))))))))
user=>(find-needle "|" "hay|stack")
[\| (\| \s \t \a \c \k) (\h \a \y)]
; makes a simple template function that can be used in mustache way: http://mustache.github.com/
(defn template [tpl env]
(loop [tpl tpl
env env]
(cond (empty? env)
tpl
:else
(let [[key value] (first env)]
(recur (try (clojure.string/replace tpl
(re-pattern (str "\\{\\{" (name key) "\\}\\}"))
value)
(catch Exception e tpl))
(rest env))))))
(defmacro loop
"Evaluates the exprs in a lexical context in which the symbols in
the binding-forms are bound to their respective init-exprs or parts
therein. Acts as a recur target."
{:added "1.0"}
[bindings & body]
(assert-args loop
(vector? bindings) "a vector for its binding"
(even? (count bindings)) "an even number of forms in binding vector")
(let [db (destructure bindings)]
(if (= db bindings)
`(loop* ~bindings ~@body)
(let [vs (take-nth 2 (drop 1 bindings))
bs (take-nth 2 bindings)
gs (map (fn [b] (if (symbol? b) b (gensym))) bs)
bfs (reduce (fn [ret [b v g]]
(if (symbol? b)
(conj ret g v)
(conj ret g v b g)))
[] (map vector bs vs gs))]
`(let ~bfs
(loop* ~(vec (interleave gs gs))
(let ~(vec (interleave bs gs))
~@body)))))))
Comments top
4 comment(s) for loop.
"Acts as a recur target."
What's a recur target? A recurring target? A recursive target? I'm not a big fan of abbreviations or ambiguous terms.
Wouldn't it be awesome if a script could annotate all occurrences of glossary terms? Or automatically wrap glossary terms in anchor tags linking to their definition?
The problem
loopis trying to solve is that recursively calling the same function on the JVM is expensive and doesn't scale. It might work if your data structure is a thousand levels deep but it will fail badly with a depth of millions of levels.What is not possible on the JVM is what is called "tail-call optimization".
loopis like awhileloop in java, except that if you don't callrecur(with the correct number of arguments) the loop will exit. In while-loop terms,recuravoids that abreakstatement is executed.int counter = 0; while (true) { if (counter < 10) { // recur counter = inc(counter); } else { break; } }In that sense
loopis a recur target as in "target for recursion".int counter = 0; while (true) { if (counter < 10) { // recur counter = inc(counter); } else { break; } }In that sense `loop` is a recur target as in "target for recursion".I wish the word recur in this document linked to the recur function. That'd be... awesome.
It really should (and will) show up in the 'vars in' section.
The problem is that recur is a special form, and is not parsed out correctly like other vars. This will be fixed in the future.