1.3.0 permalink Arrow_down_16x16
  • (for seq-exprs body-expr)
List comprehension. Takes a vector of one or more
binding-form/collection-expr pairs, each followed by zero or more
modifiers, and yields a lazy sequence of evaluations of expr.
Collections are iterated in a nested fashion, rightmost fastest,
and nested coll-exprs can refer to bindings created in prior
binding-forms. Supported modifiers are: :let [binding-form expr ...],
:while test, :when test.

(take 100 (for [x (range 100000000) y (range 1000000) :while (< y x)] [x y]))

11 Examples top

  • user=> (for [x [0 1 2 3 4 5]
                 :let [y (* x 3)]
                 :when (even? y)]
             y)
    (0 6 12)
    
  • user=> (def digits (seq [1 2 3]))
    user=> (for [x1 digits x2 digits] (* x1 x2))
    (1 2 3 2 4 6 3 6 9)
  • user=> (for [x ['a 'b 'c] 
                 y [1 2 3]] 
              [x y])
    
    ([a 1] [a 2] [a 3] [b 1] [b 2] [b 3] [c 1] [c 2] [c 3])
  • user=> (for [x (range 1 6) 
                 :let [y (* x x) 
                       z (* x x x)]] 
             [x y z])
    
    ([1 1 1] [2 4 8] [3 9 27] [4 16 64] [5 25 125])
    
  • (for [x (range 3 7)] 
        (* x x))
    
    (9 16 25 36)
  • user=> (for [[x y] '([:a 1] [:b 2] [:c 0]) :when (= y 0)] x)
    (:c)
    
  • ; Demonstrating difference between :when and :while
    
    user=> (time (dorun (for [x (range 1000) y (range 10000) :when (> x y)] [x y])))
    "Elapsed time: 2898.908 msecs"
    nil
    
    user=> (time (dorun (for [x (range 1000) y (range 10000) :while (> x y)] [x y])))
    "Elapsed time: 293.677 msecs"
    nil
  • ; Demonstrating difference between :when and :while
    user=> (for [x (range 3) y (range 3) :when (not= x y)] [x y])
    ([0 1] [0 2] [1 0] [1 2] [2 0] [2 1])
    user=> (for [x (range 3) y (range 3) :while (not= x y)] [x y])
    ([1 0] [2 0] [2 1])
  • ;; More examples illustrating the difference between :when and :while
    
    ;; Simple but inefficient method of checking whether a number is
    ;; prime.
    user=> (defn prime? [n]
             (not-any? zero? (map #(rem n %) (range 2 n))))
    #'user/prime?
    
    user=> (range 3 33 2)
    (3 5 7 9 11 13 15 17 19 21 23 25 27 29 31)
    
    ;; :when continues through the collection even if some have the
    ;; condition evaluate to false, like filter
    user=> (for [x (range 3 33 2) :when (prime? x)]
             x)
    (3 5 7 11 13 17 19 23 29 31)
    
    ;; :while stops at the first collection element that evaluates to
    ;; false, like take-while
    user=> (for [x (range 3 33 2) :while (prime? x)]
             x)
    (3 5 7)
    
    ;; The examples above can easily be rewritten with filter or
    ;; take-while.  When you have a for with multiple binding forms, so
    ;; that the iteration occurs in a nested fashion, it becomes possible
    ;; to write something briefly with 'for' that would be more verbose or
    ;; unwieldy with nested filter or take-while expressions.
    
    user=> (for [x (range 3 17 2) :when (prime? x)
                 y (range 3 17 2) :when (prime? y)]
             [x y])
    ([ 3 3] [ 3 5] [ 3 7] [ 3 11] [ 3 13]
     [ 5 3] [ 5 5] [ 5 7] [ 5 11] [ 5 13]
     [ 7 3] [ 7 5] [ 7 7] [ 7 11] [ 7 13]
     [11 3] [11 5] [11 7] [11 11] [11 13]
     [13 3] [13 5] [13 7] [13 11] [13 13])
    
    user=> (for [x (range 3 17 2) :while (prime? x)
                 y (range 3 17 2) :while (prime? y)]
             [x y])
    ([3 3] [3 5] [3 7]
     [5 3] [5 5] [5 7]
     [7 3] [7 5] [7 7])
    
    ;; This example only gives a finite result because of the :while
    ;; expressions.
    user=> (for [x (range) :while (< x 10) 
                 y (range) :while (<= y x)]
             [x y])
    
    ([0 0]
     [1 0] [1 1]
     [2 0] [2 1] [2 2]
     [3 0] [3 1] [3 2] [3 3]
     [4 0] [4 1] [4 2] [4 3] [4 4]
     [5 0] [5 1] [5 2] [5 3] [5 4] [5 5]
     [6 0] [6 1] [6 2] [6 3] [6 4] [6 5] [6 6]
     [7 0] [7 1] [7 2] [7 3] [7 4] [7 5] [7 6] [7 7]
     [8 0] [8 1] [8 2] [8 3] [8 4] [8 5] [8 6] [8 7] [8 8]
     [9 0] [9 1] [9 2] [9 3] [9 4] [9 5] [9 6] [9 7] [9 8] [9 9])
    
  • ;; Here are a couple of examples where the only difference is where
    ;; the :while is placed, but it makes a significant difference in the
    ;; behavior.
    
    ;; When x=2 y=1 is reached, :while (<= x y) evaluates false, so all
    ;; further items in the y collection are skipped.  When x=3 y=1 is
    ;; reached, the same thing happens.
    
    user=> (for [x [1 2 3]
                 y [1 2 3]
                 :while (<= x y)
                 z [1 2 3]]
             [x y z])
    ([1 1 1] [1 1 2] [1 1 3]
     [1 2 1] [1 2 2] [1 2 3]
     [1 3 1] [1 3 2] [1 3 3])
    
    ;; This is different.  When x=2 y=1 z=1 is reached, :while (<= x y)
    ;; evaluates false, but since the :while is after the binding for z,
    ;; all further items in the z collection are skipped.  Then x=2 y=2
    ;; z=1 is tried, where the while expresssion evaluates true.
    
    user=> (for [x [1 2 3]
                 y [1 2 3]
                 z [1 2 3]
                 :while (<= x y)]
             [x y z])
    ([1 1 1] [1 1 2] [1 1 3]
     [1 2 1] [1 2 2] [1 2 3]
     [1 3 1] [1 3 2] [1 3 3]
     [2 2 1] [2 2 2] [2 2 3]
     [2 3 1] [2 3 2] [2 3 3]
     [3 3 1] [3 3 2] [3 3 3])
    
  • (defn all-files-present?
    "Takes a list of real file names, and returns a map of files present 1
    and not present 0."
    [file-seq]
    (for [fnam file-seq
     :let [stat-map {(keyword fnam) (look-for fnam "f")}]]
      stat-map))
    
    (into {}  (all-files-present? '("Makefile" "build.sh" "real-estate.csv")))
    
    {:Makefile 1, :build.sh 1, :real-estate.csv 0}
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:4074 top

(defmacro for
  "List comprehension. Takes a vector of one or more
   binding-form/collection-expr pairs, each followed by zero or more
   modifiers, and yields a lazy sequence of evaluations of expr.
   Collections are iterated in a nested fashion, rightmost fastest,
   and nested coll-exprs can refer to bindings created in prior
   binding-forms.  Supported modifiers are: :let [binding-form expr ...],
   :while test, :when test.

  (take 100 (for [x (range 100000000) y (range 1000000) :while (< y x)] [x y]))"
  {:added "1.0"}
  [seq-exprs body-expr]
  (assert-args for
     (vector? seq-exprs) "a vector for its binding"
     (even? (count seq-exprs)) "an even number of forms in binding vector")
  (let [to-groups (fn [seq-exprs]
                    (reduce1 (fn [groups [k v]]
                              (if (keyword? k)
                                (conj (pop groups) (conj (peek groups) [k v]))
                                (conj groups [k v])))
                            [] (partition 2 seq-exprs)))
        err (fn [& msg] (throw (IllegalArgumentException. ^String (apply str msg))))
        emit-bind (fn emit-bind [[[bind expr & mod-pairs]
                                  & [[_ next-expr] :as next-groups]]]
                    (let [giter (gensym "iter__")
                          gxs (gensym "s__")
                          do-mod (fn do-mod [[[k v :as pair] & etc]]
                                   (cond
                                     (= k :let) `(let ~v ~(do-mod etc))
                                     (= k :while) `(when ~v ~(do-mod etc))
                                     (= k :when) `(if ~v
                                                    ~(do-mod etc)
                                                    (recur (rest ~gxs)))
                                     (keyword? k) (err "Invalid 'for' keyword " k)
                                     next-groups
                                      `(let [iterys# ~(emit-bind next-groups)
                                             fs# (seq (iterys# ~next-expr))]
                                         (if fs#
                                           (concat fs# (~giter (rest ~gxs)))
                                           (recur (rest ~gxs))))
                                     :else `(cons ~body-expr
                                                  (~giter (rest ~gxs)))))]
                      (if next-groups
                        #_"not the inner-most loop"
                        `(fn ~giter [~gxs]
                           (lazy-seq
                             (loop [~gxs ~gxs]
                               (when-first [~bind ~gxs]
                                 ~(do-mod mod-pairs)))))
                        #_"inner-most loop"
                        (let [gi (gensym "i__")
                              gb (gensym "b__")
                              do-cmod (fn do-cmod [[[k v :as pair] & etc]]
                                        (cond
                                          (= k :let) `(let ~v ~(do-cmod etc))
                                          (= k :while) `(when ~v ~(do-cmod etc))
                                          (= k :when) `(if ~v
                                                         ~(do-cmod etc)
                                                         (recur
                                                           (unchecked-inc ~gi)))
                                          (keyword? k)
                                            (err "Invalid 'for' keyword " k)
                                          :else
                                            `(do (chunk-append ~gb ~body-expr)
                                                 (recur (unchecked-inc ~gi)))))]
                          `(fn ~giter [~gxs]
                             (lazy-seq
                               (loop [~gxs ~gxs]
                                 (when-let [~gxs (seq ~gxs)]
                                   (if (chunked-seq? ~gxs)
                                     (let [c# (chunk-first ~gxs)
                                           size# (int (count c#))
                                           ~gb (chunk-buffer size#)]
                                       (if (loop [~gi (int 0)]
                                             (if (< ~gi size#)
                                               (let [~bind (.nth c# ~gi)]
                                                 ~(do-cmod mod-pairs))
                                               true))
                                         (chunk-cons
                                           (chunk ~gb)
                                           (~giter (chunk-rest ~gxs)))
                                         (chunk-cons (chunk ~gb) nil)))
                                     (let [~bind (first ~gxs)]
                                       ~(do-mod mod-pairs)))))))))))]
    `(let [iter# ~(emit-bind (to-groups seq-exprs))]
        (iter# ~(second seq-exprs)))))
Vars in clojure.core/for:
Used in 0 other vars

Comments top

3 comment(s) for for.

Take careful note of the description's wording:

binding-form/collection-expr pairs, 
each followed by zero or more modifiers

A consequence is that the binding list may not begin with a modifier, i.e a :let, :when or :while!

The following example is illegal syntax:

(for [:let [a 1] b (range 5)] 
  {a b})

While it might sometimes be convenient to start a for with a :let to reduce code clutter, the "correct" procedure is to nest the for in a "proper" let, like this:

(let [a 1]
  (for [b (range 5)] 
    {a b}))

Similarly, a :when is better represented by nesting in an if.

The fifth example should probably be shown in first position, it's the most straightforward and readable for a beginner :

(for [x (range 3 7)] (* x x))

"Sequence comprehension", not "list comprehension".

Log in to add a comment.