tags:

views:

309

answers:

2

I want to perform the following nested operations until the experession is satisfied.. Is there a :until keyword which stops doing further operations when the condition matches.?

This command generates the Pythagoran Triplet 3 4 5. I dont want it to do anything else once it gets to that sequence of numbers.

(for [a (range 1 100)
      b (range 1 100)
      c (list (Math/sqrt (+ (Math/pow (int a) 2) (Math/pow (int b) 2))))
      :when (= 12 (+ a b c))]
     (list a b c))
+5  A: 

:while is a short-circuiting test in for expressions. List elements will be generated until the first time it encounters a failing test.

In your case

(for [<code omitted> :while (not (= 12 (+ a b c)))] (list a b c))

would stop generating elements as soon as it found the triplet summing to 12.

One problem though, it doesn't do what you're expecting. The triplet itself would not be part of the result since it failed the test.

A list comprehension may not be the best solution if you are only looking for a single matching result. Why not just use a loop?

(loop [xs (for [a (range 1 100)
                b (range 1 100)] [a, b])]
  (when (seq xs)
    (let [[a, b] (first xs)
          c (Math/sqrt (+ (Math/pow (int a) 2)
                          (Math/pow (int b) 2)))]
      (if (not (= 12 (+ a b c)))
        (recur (next xs))
        (list a b c)))))
alanlcode
I learned a lot from that code. Just a couple of questions: 1.What type does (for....) generate? 2.Why do you need to (seq xs)?
kunjaan
1) A lazy sequence.2) To check for the case when xs is empty: (seq xs) will return nil when xs is empty, thus failing the test and exiting the loop. This is a fairly common idiom in Clojure.
alanlcode
A: 

Since for yields a lazy sequence you will get the desired result by picking the first element:

(first (for [a (range 1 100)
             b (range 1 100)
             c (list (Math/sqrt (+ (Math/pow (int a) 2) 
                                   (Math/pow (int b) 2))))
             :when (= 12 (+ a b c))]
          (list a b c))

Only the first element of the generated list is computed due to laziness, which can be demonstrated with a side effect:

user=> (first
        (for [a (range 1 100)
              b (range 1 100)
              c (list (Math/sqrt (+ (Math/pow (int a) 2) 
                                    (Math/pow (int b) 2))))
              :when (= 12 (+ a b c))]
          (do (println "working...")
              (list a b c))))
working...
(3 4 5.0)

(for ...) comes with a :let modifier so there is no need to wrap c in a list:

(for [a (range 1 100)
      b (range 1 100)
      :let [c (Math/sqrt (+ (Math/pow (int a) 2) 
                            (Math/pow (int b) 2)))]
      :when (= 12 (+ a b c))]
  (list a b c))
Jonas