You're viewing version 1.2.0 of loop. The latest stable version of Clojure Core is 1.3.0.
1.2.0 Arrow_down_16x16
  • (loop bindings & body)
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.

5 Examples top

  • ;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)))))
    
  • (loop [x 10]
      (when (> x 1)
        (println x)
        (recur (- x 2))))
  • (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))))))
Log in to add / edit an example.

See Also top

Log in to add a see also.

Plus_12x12 Minus_12x12 Source clojure/core.clj:3533 top

(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)))))))
Vars in clojure.core/loop: = count defmacro even? interleave let vec vector? concat list seq

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 loop is 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". loop is like a while loop in java, except that if you don't call recur (with the correct number of arguments) the loop will exit. In while-loop terms, recur avoids that a break statement is executed.

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.

Log in to add a comment.