ClojureDocs

Nav

Namespaces

loop

clojure.core

Available since 1.0 (source)
  • (loop [bindings*] exprs*)
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.
14 Examples
;; 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 
         "Something like re-seq"
         [re string]
         (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))))

;;=> 10 8 6 4 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))))))
(loop [iter 1
       acc  0]
  (if (> iter 10)
    (println acc)
    (recur (inc iter) (+ acc iter))))

;; => 55
;; sum from 1 to 10
;; loop is the recursion point for recur. The symbols in loop's 
;; binding-forms are bound to their respective init-exprs and 
;; rebound to the values of recur's exprs before the next execution 
;; of loop's body.

;; calculate the factorial of n

(loop [n (bigint 5), accumulator 1]
  (if (zero? n)
    accumulator  ; we're done
    (recur (dec n) (* accumulator n))))

;;=> 120N


;; square each number in the vector

(loop [xs (seq [1 2 3 4 5])
       result []]
  (if xs
    (let [x (first xs)]
      (recur (next xs) (conj result (* x x))))
    result))

;; => [1 4 9 16 25]
;; A loop that sums the numbers 10 + 9 + 8 + ...

;; Set initial values count (cnt) from 10 and down
(loop [sum 0 cnt 10]
  ;; If count reaches 0 then exit the loop and return sum
  (if (= cnt 0)
    sum
    ;; Otherwise add count to sum, decrease count and 
    ;; use recur to feed the new values back into the loop
    (recur (+ cnt sum) (dec cnt))))
(loop [i 0]  
  (when (< i 5)    
    (println i)    
    (recur (inc i)))) ; loop i will take this value
;; Iterating over a collection using loop

;; 1. First call (seq xs) on the given argument and then check for nil 
;; 2. Then call next/first and use these.

(loop [xs (seq [1 2 3 4 5])
       result []]
  (if xs
    (let [x (first xs)]
      (recur (next xs) (conj result (* x x))))
    result))

;; the same loop can be written using destructing,
;; but the compiler will generate two consecutive
;; seq calls and is slightly less efficient.

(loop [[x & r :as xs] (seq [])
       result []]
  (if xs
    (recur r (conj result (* x x)))
    result))
;;basic loop example #1

(loop [x 0
       result []]
  (if (< x 10)
    (recur
      (inc x)
      (conj result x)) result))
;;[0 1 2 3 4 5 6 7 8 9]

;;basic loop example #2
(def citrus-list ["lemon" "orange" "grapefruit"])

(defn display-citrus [citruses]
  (loop [[citrus & citruses] citruses]
    (println citrus)
    (if citrus (recur citruses))))

(display-citrus citrus-list)
;;loop general strategy
;;given a collection of numbers [1 2 3 4 5],
;;return a new collection [10 20 30 40 50]


(def my-vector [1 2 3 4 5])

(defn my-new-vector
  [coll]
  (loop [remain coll
         final-vec []]
    (if (empty? remain)
      final-vec
      (let [[unit & remaining] remain]
        (recur remaining
               (into final-vec [(* 10 unit)]))))))

(my-new-vector my-vector)
;;[10 20 30 40 50]

;;to sum all the elements from the newly created collection:

(reduce + (my-new-vector my-vector))
;;150
;; loop -> recur sample with fizzbuzz code.

(defn fizzbuzz
  [n]
  (loop [f []
         i 1]
    (if (< n i)
      f
      (recur (conj f (cond
                       (zero? (mod i 15)) "fizzbuzz"
                       (zero? (mod i 3)) "fizz"
                       (zero? (mod i 5)) "buzz"
                       :else i))
             (inc i)))))

(println (fizzbuzz 100))
;; output is 
;; [1 2 fizz 4 buzz fizz 7 8 fizz buzz 11 fizz 13 14 fizzbuzz 16 17 fizz 19 buzz fizz 22 23 fizz buzz 26 fizz 28 29 fizzbuzz 31 32 fizz 34 buzz fizz 37 38 fizz buzz 41 fizz 43 44 fizzbuzz 46 47 fizz 49 buzz fizz 52 53 fizz buzz 56 fizz 58 59 fizzbuzz 61 62 fizz 64 buzz fizz 67 68 fizz buzz 71 fizz 73 74 fizzbuzz 76 77 fizz 79 buzz fizz 82 83 fizz buzz 86 fizz 88 89 fizzbuzz 91 92 fizz 94 buzz fizz 97 98 fizz buzz]
;; loop accepts pre and post conditions
(loop [left -1.0
       right  1.0]
  {:pre [(< left right)]}
  ...)
See Also

Evaluates the exprs in order, then, in parallel, rebinds the bindings of the recursion point to the ...

Added by kumarshantanu

trampoline can be used to convert algorithms requiring mutual recursion without stack consumption....

Added by kumarshantanu

Evaluates test. If logical true, evaluates body in an implicit do.

Repeatedly executes body while test expression is true. Presumes some side-effect will cause test ...

Added by Phalphalak

List comprehension. Takes a vector of one or more binding-form/collection-expr pairs, each follow...

Added by mars0i

Repeatedly executes body (presumably for side-effects) with bindings and filtering as provided by ...

Added by mars0i

f should be a function of 2 arguments. If val is not supplied, returns the result of applying f to...

Added by mars0i

bindings => name n Repeatedly executes body (presumably for side-effects) with name bound to in...

Added by mars0i
6 Notes
    By , created 14.7 years ago, updated 14.7 years ago

    "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?

    By , created 14.7 years ago, updated 14.7 years ago

    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".

    By , created 14.7 years ago

    I wish the word recur in this document linked to the recur function. That'd be... awesome.

    By , created 14.7 years ago, updated 14.7 years ago

    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.

    By , created 10.2 years ago, updated 10.2 years ago

    Majority of loops that novices write can be expressed more elegantly using 3 fundamental functions map, filter and reduce. Or using list comprehension for.

    By , created 9.9 years ago

    To be clear, there's no technical reason the JVM can't support tail recursion (despite some complications to do with call stacks and security) - it just doesn't happen to support them currently.

    People have been requesting this enhancement for at least a decade - here's one proposal, for example.