Search code examples
clojureclojure-contrib

repeatedly apply a function until test no longer yields true


I wrote this code to nest a function n times and am trying to extend the code to handle a test. Once the test returns nil the loop is stopped. The output be a vector containing elements that tested true. Is it simplest to add a while loop in this case? Here is a sample of what I've written:

(defn nester [a inter f]
(loop [level inter expr a]
    (if (= level 0) expr
    (if (> level 0) (recur (dec level) (f expr))))))

An example input would be an integer 2, and I want to nest the inc function until the output is great than 6. The output should be [2 3 4 5 6 7].


Solution

  • (defn nester [a inter f test-fn]
      (loop [level inter
             expr a]
        (if (or (zero? level)
                (nil? (test-fn expr)))
          expr
          (recur (dec level)
                 (f expr)))))
    

    If you also accept false (additionally to nil) from your test-fn, you could compose this more lazily:

    (defn nester [a inter f test-fn]
      (->> (iterate f a)
           (take (inc inter))
           (drop-while test-fn)
           first))
    

    EDIT: The above was answered to your initial question. Now that you have specified completely changed the meaning of your question:

    If you want to generate a vector of all iterations of a function f over a value n with a predicate p:

    (defn nester [f n p]
      (->> (iterate f n)
           (take-while p)
           vec))
    
    (nester inc 2 (partial > 8)) ;; predicate "until the output is greater than six"
                                 ;; translated to "as long as 8 is greater than
                                 ;; the output"
    
    => [2 3 4 5 6 7]