Dan Jameson
Dan Jameson

Reputation: 1520

How can I change the :while condition on a Clojure for-loop while it is running?

I'm searching for the biggest number meeting some condition, out of the set of numbers that are the product of all three-digit numbers.

The straightforward way is this:

(apply max (filter 
            my-test? 
            (for [x (range 100 1000)
                  y (range x 1000)]
              (* x y))))

But this involves calculating half of all 900*900 products. If I find, e.g., (* 380 455) matches my my-test? predicate, I don't need to search any products where either factor is less than 380. Of course, I will also want to search the list from the biggest to the smallest if I'm doing this.

In psuedo-Clojure, I want to say something like this:

(for [x (range 999 99 -1) :while (>= x P)
      y (range x 99 -1) :while (>= y P)]
  (* x y))

where P is something I magically set to (min x y) each time I find a match.

Is there a way to do this in Clojure?

(I realize I could search the numbers in a diagonal fashion, given this specific problem I've set up. However, I'm now trying to solve for the general case of figuring out how to tell the for-loop that some its branches need pruned.)

Upvotes: 2

Views: 150

Answers (2)

Dan Jameson
Dan Jameson

Reputation: 1520

@omeil suggested a loop/recur process, and this works much better. for loops just aren't built for this.

(loop [x 999 y 999 min 99 candidates []]
    (cond (<= x min)          candidates ; exit condition
          (<= y min)          (recur (dec x) 999 min candidates) ; step x
          (my-test? (* x y)) (recur x (dec y) (max min y) (conj candidates (* x y)))
          :else               (recur x (dec y) min candidates) ; step y
     ))

Upvotes: 5

Shepmaster
Shepmaster

Reputation: 430554

A very ugly solution would be to use an atom:

(let [P (atom 0)]
  (for [x (range 999 99 -1) :while (>= x @P)
        y (range x 99 -1)   :while (>= y @P)]
    (do
      (reset! P (+ x y))
      (* x y))))

One thing that I think will get in the way is that the for loop doesn't really care what the "output" is, so I am unable to see how it would be able to get that information natively.

The "right" solution is probably a recursive one that allows you to pass in (a.k.a. update) your minimum as you know more.

Upvotes: 1

Related Questions