Search code examples
clojuredeclarative

Mysterious Clojure function


I would like to write a clojure function that has the following behaviour :

  (take 4 (floyd))
  => '((1) (2 3) (4 5 6) (7 8 9 10))

  (take 3 (floyd))
  => '((1) (2 3) (4 5 6))

  (take 1 (floyd))
  => '((1)))

I tried using partition and partition-all to validate these tests however i couldn't get the right solution. If you have any idea of how to do it, i would really appreciate a little help. I started using clojure a few weeks ago and still have some issues.
Thanks


Solution

  • it is not possible to solve it with partition / partition-all, since they split your sequence into predefined size chunks.

    What you can do, is to employ recursive lazy function for that:

    user> (defn floyd []
            (letfn [(f [n rng]
                      (cons (take n rng)
                            (lazy-seq (f (inc n) (drop n rng)))))]
              (f 1 (iterate inc 1))))
    #'user/floyd
    
    user> (take 1 (floyd))
    ;;=> ((1))
    
    user> (take 2 (floyd))
    ;;=> ((1) (2 3))
    
    user> (take 3 (floyd))
    ;;=> ((1) (2 3) (4 5 6))
    
    user> (take 4 (floyd))
    ;;=> ((1) (2 3) (4 5 6) (7 8 9 10))
    

    another variant can use similar approach, but only track chunk-start/chunk-size:

    user> (defn floyd []
            (letfn [(f [n start]
                      (cons (range start (+ start n))
                            (lazy-seq (f (inc n) (+ start n)))))]
              (f 1 1)))
    

    another approach is to use clojure's collection operating functions:

    user> (defn floyd-2 []        
            (->> [1 1]
                 (iterate (fn [[start n]]
                            [(+ n start) (inc n)]))
                 (map (fn [[start n]] (range start (+ start n))))))
    #'user/floyd-2
    
    user> (take 4 (floyd-2))
    ;;=> ((1) (2 3) (4 5 6) (7 8 9 10))
    
    user> (take 5 (floyd-2))
    ;;=> ((1) (2 3) (4 5 6) (7 8 9 10) (11 12 13 14 15))
    
    user> (take 1 (floyd-2))
    ;;=> ((1))