What is the meaning of :while in a for ?

169 views
Skip to first unread message

nicola...@gmail.com

unread,
Aug 21, 2012, 6:28:50 AM8/21/12
to clojure
Dear all,

What is the meaning of :while in a for?
I understand :when, and also that :while jumps more element when the
condition is not met,
but where does it jump to exactly?

Best regards,

Nicolas.

Jim - FooBar();

unread,
Aug 21, 2012, 7:10:18 AM8/21/12
to clo...@googlegroups.com

Jonas

unread,
Aug 21, 2012, 7:11:01 AM8/21/12
to clo...@googlegroups.com
Both `:when` and `:while` tests if an element should be used when building the resulting sequence. If the `:when` part evaluates to false (or nil) the element is skipped and the list comprehension continues with the next element in the source sequence. `:while` on the other hand ends the list comprehension when the test evaluates to false (or nil) and returns the sequence generated thus far.

Note the difference between

  (for [x [2 4 6 7 8] :when (even? x)] x) ; => (2 4 6 8)

and

  (for [x [2 4 6 7 8] :while (even? x)] x) ; => (2 4 6)

Hope that helps.

Jonas

Tassilo Horn

unread,
Aug 21, 2012, 7:15:11 AM8/21/12
to clo...@googlegroups.com
"nicola...@gmail.com" <nicola...@gmail.com> writes:

Hi Nicolas,

> What is the meaning of :while in a for?
> I understand :when, and also that :while jumps more element when the
> condition is not met,

Yes. With :when every combination is checked, whereas with :while, the
remaining combinations are skipped if it is falsy.

> but where does it jump to exactly?

Good question. I think it skips the preceeding binding form. At least,
that's what I can infer from some examples.

(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])

So when we hit [2 1 _] which is false, the 2 and 3 for y are skippd.
Thus the next binding is [3 1 _] which is false again. And there are no
more.

(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])

Here, as soon as we hit [2 1 1] which is false, the remainders of z are
skipped leading to [2 2 1] which is true again. Next we hit [3 1 1]
which is false, so we skip to [3 2 1] which is false again, so we skip
to [3 3 1] which is true again.

HTH,
Tassilo

Tassilo Horn

unread,
Aug 21, 2012, 7:22:39 AM8/21/12
to clo...@googlegroups.com
Jonas <jonas....@gmail.com> writes:

> `:while` on the other hand ends the list comprehension when the test
> evaluates to false (or nil) and returns the sequence generated thus
> far.

No, it's perfectly possible to have a comprehension with a :while that
generates more elements after :while evaluated to false. :while skips
some bindings, but it doesn't need to skip all of them. See my original
reply to Nicolas.

Bye,
Tassilo

Jonas

unread,
Aug 21, 2012, 7:39:58 AM8/21/12
to clo...@googlegroups.com

Thanks for pointing that out. I realize now that my understanding of the `for` macro is not complete. I wonder if complex `for`-expressions hurt code readability or is the alternative (using map+filter) even more complicated?
 

Bye,
Tassilo

David Powell

unread,
Aug 21, 2012, 7:41:24 AM8/21/12
to clo...@googlegroups.com
 
No, it's perfectly possible to have a comprehension with a :while that
generates more elements after :while evaluated to false.  :while skips
some bindings, but it doesn't need to skip all of them.  See my original
reply to Nicolas.


Wow - I never knew that.  That isn't at all obvious from the sparse doc string.

(Is that actually a bug, I wonder? - it seems like a pretty useless behaviour?)

-- 
Dave

Tassilo Horn

unread,
Aug 21, 2012, 9:30:08 AM8/21/12
to clo...@googlegroups.com
David Powell <djpo...@djpowell.net> writes:

>> No, it's perfectly possible to have a comprehension with a :while that
>> generates more elements after :while evaluated to false. :while skips
>> some bindings, but it doesn't need to skip all of them. See my original
>> reply to Nicolas.
>
> Wow - I never knew that. That isn't at all obvious from the sparse
> doc string.

Yes, that's really not really easy to get right.

> (Is that actually a bug, I wonder? - it seems like a pretty useless
> behaviour?)

I think it's useful. Check my second example with the :while as last
form. Although these two result in the same value

(for [x [1 2 3] y [1 2 3] z [1 2 3] :while (<= x y)] [x y z])
(for [x [1 2 3] y [1 2 3] z [1 2 3] :when (<= x y)] [x y z])

the former performs a bit better because the version with :when needs to
test the :when form for any binding while the :while version doesn't
evaluates the test less frequently.

Another example counting the number of test evaluations:

--8<---------------cut here---------------start------------->8---
user> (def a (atom 0))
#'user/a
user> (time (dorun (for [x (range 1000)
y (range 100)
z [1 2 3]
:when (do (swap! a inc) (<= x y))]
[x y z])))
"Elapsed time: 205.593919 msecs"
nil
user> @a
300000
user> (def a (atom 0))
#'user/a
user> (time (dorun (for [x (range 1000)
y (range 100)
z [1 2 3]
:while (do (swap! a inc) (<= x y))]
[x y z])))
"Elapsed time: 146.163655 msecs"
nil
user> @a
110100
--8<---------------cut here---------------end--------------->8---

So if your :while/:when test is expensive, that might make a difference
performance-wise.

Bye,
Tassilo

Tassilo Horn

unread,
Aug 21, 2012, 9:55:09 AM8/21/12
to clo...@googlegroups.com
Jonas <jonas....@gmail.com> writes:

Hi Jonas,

>> > `:while` on the other hand ends the list comprehension when the
>> > test evaluates to false (or nil) and returns the sequence generated
>> > thus far.
>>
>> No, it's perfectly possible to have a comprehension with a :while
>> that generates more elements after :while evaluated to false. :while
>> skips some bindings, but it doesn't need to skip all of them. See my
>> original reply to Nicolas.
>
> Thanks for pointing that out. I realize now that my understanding of
> the `for` macro is not complete. I wonder if complex `for`-expressions
> hurt code readability or is the alternative (using map+filter) even
> more complicated?

IMO, `for` provides a very concise and readable syntax for a rather
complicated thing. Doing that with map/filter/closures by hand is much
more complicated and verbose.

And with the exception of :while, it's easy to understand. So I don't
want to miss it.

Bye,
Tassilo

Herwig Hochleitner

unread,
Aug 21, 2012, 10:34:47 AM8/21/12
to clo...@googlegroups.com
Even though this thread is almost over, I'll quickly chime in and hopefully add some clarity.

I've only stumbled over for's :while because of this; I like it. It's the only modifier in a for, that can actually stop evaluation of its source sequence. It's akin to take-while.
Behold the generation of a triangular matrix (output formatting by me):

(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])

Notice, that (range) generates an infinite lazy sequence, which is only stopped by virtue of :while.

The gist is: :when is akin to filter, :while is akin to take-while

kind regards

nicola...@gmail.com

unread,
Aug 21, 2012, 11:34:59 AM8/21/12
to clo...@googlegroups.com
I understand now.
The documentation could be clearer on that.
Your triangular example is very clear.

Arie van Wingerden

unread,
Aug 21, 2012, 6:50:50 AM8/21/12
to clo...@googlegroups.com
Extra restrictions on (range of values of) variables used in the for.
See here:

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

nicola...@gmail.com

unread,
Aug 22, 2012, 12:08:59 PM8/22/12
to clo...@googlegroups.com
=
On Tue, Aug 21, 2012 at 11:50 AM, Arie van Wingerden <xap...@gmail.com> wrote:
> Extra restrictions on (range of values of) variables used in the for.
> See here:
> http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/for
>
The link says nothing about the meaning of the modifiers.
(I agree it should, though.)

Andy Fingerhut

unread,
Aug 23, 2012, 11:22:56 AM8/23/12
to clo...@googlegroups.com
I've added some examples of :when and :while, including those given by Herwig and Tassilo in this thread, at ClojureDocs:

http://clojuredocs.org/clojure_core/clojure.core/for

Note: Anyone with a free account can add/edit examples on that site.

Andy

Ambrose Bonnaire-Sergeant

unread,
Aug 23, 2012, 11:28:12 AM8/23/12
to clo...@googlegroups.com
Thanks Andy, that's awesome.

nicola...@gmail.com

unread,
Aug 23, 2012, 11:29:04 AM8/23/12
to clo...@googlegroups.com
That's amazing.
Thanks.
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en



--
Sent from an IBM Model M, 15 August 1989.
Reply all
Reply to author
Forward
0 new messages