Last time I explained lazy-when functionand how it helps me in solving euler problems. Now I`ll introduce additional similar functions, and then as always, I`m gonna use them in a couple of euler problems.
First, consider all the times when you need lazy functions that check if the number has more then one property. I wanted to re-write lazy-when to accept a collection of predicates and check the number on all of them:
(defn all? [pred-coll]I used all? to transform predicate function. It checks if pred is a collection. If not, it returns pred, but if it is then it returns another function that checks the number on all predicates. The rest of the function is pretty-much same.
(if (coll? pred-coll) (fn [n] (every? #(% n) pred-coll)) pred-coll))
(defn lazy-when
([pred s] (lazy-when pred s inc))
([pred n next-n]
(let [pred-f (all? pred)]
(if-not (pred-f n)
(recur pred-f (next-n n) next-n)
(lazy-seq (cons n (lazy-when pred-f (next-n n) next-n)))))))
Quite often, I need the first number from the lazy seq after some other number:
(defn lazy-next [pred s & next]
(let [next (if (nil? next) inc (first next))]
(let [s (if ((all? pred) s) (next s) s)]
(first (lazy-when pred s next)))))
With next I did the same thing as in lazy-when. S transformation, however, is interesting. If pred returns true for s, then lazy-next would return that value too. And if you have a loop where you recur on each number from a lazy function, that loop would become infinite, and you usualy don't want that. So s is transformed to the next value that would otherwise be checked only if pred returns false for s.
This function will not serve for prime numbers. If I offer s 2 and next step #(+ % 2), then it would check even numbers and never return. However, if I offer inc for the next step, then my lazy-next becomes much slower. That`s why I created next-prime, as the special case of lazy-next when predicate is prime?
(defn next-prime [prime]Finally, euler usually looks for the sum of this-and-that numbers under x. For that reason, I wrote the following functions:
(if (= prime 2) 3 (lazy-next prime? prime #(+ % 2))))
(defn lazy-top [pred s end until? & next]
(let [next (if (nil? next) inc (first next))]
(take-while #(until? % end) (lazy-when pred s next))))
(defn lazy-sum [pred s end until? & next]
(let [next (if (nil? next) inc (first next))]
(reduce + (lazy-top pred s end until? next))))
Little more than one-liners, but the pattern is there often enought so why not have it as a function?
Now, with those, to solve the problem from the last post all it takes is:
(time (println (+ 2 (lazy-sum prime? 3 (reduce * 2 (repeat 6 10)) < #(+ 2 %) ))))
Lets try something else. Problem 046 I only need to discover if the number can be written as the sum of a prime and twice a square, and I have it all:
(defn goldbach? [n]
(loop [prime 2 root 1]
(let [goldbach (+ prime (* 2 root root))]
(cond (= n goldbach) true
(> prime (dec n)) false
(> goldbach n) (recur (next-prime prime) 1)
:else (recur prime (inc root))))))
(time (println (lazy-next [#(not (prime? %)) #(not (goldbach? %))] 33 #(+ % 2))))