Clojure懒惰序列问题

时间:2013-11-07 00:58:54

标签: clojure

我正在研究4clojure问题,类似的问题不断出现。我将编写一个适用于除一个测试用例之外的所有测试用例的解决方案。它通常是检查懒惰评估的人。除了最后一个测试用例之外,下面的解决方我已经尝试了各种解决方案,似乎无法在整数溢出之前停止评估。我阅读了关于“欢乐的Clojure”中关于懒惰序列的章节,但我很难实现它们。有没有经验法则我会忘记,比如不要使用循环或类似的东西?

; This version is non working at the moment, will try to edit a version that works
(defn i-between [p k coll]
  (loop [v [] coll coll]
     (let [i (first coll) coll (rest coll) n (first coll)]
        (cond (and i n)
          (let [ret (if (p i n) (cons k (cons i v)) (cons i v))]
            (recur ret coll))
          i
          (cons i v )
        :else  v))))

Problem 132

那些好奇的终极解决方案:

(fn i-between [p k coll]
  (letfn [(looper [coll]
     (if (empty? coll) coll
       (let [[h s & xs] coll 
          c (cond (and h s (p h s))
                   (list h k )
                  (and h s)
                   (list h )
                  :else (list h))]
          (lazy-cat c (looper (rest coll))))
   ))] (looper coll)))

1 个答案:

答案 0 :(得分:2)

当我考虑懒惰序列时,通常会考虑增量cons'ing

也就是说,每个递归步骤只会将单个元素添加到列表中,当然您永远不会使用loop

所以你拥有的是这样的:

(cons (generate first) (recur rest))

当包裹在lazy-seq上时,仅实现序列中所需的元素,例如。

 (take 5 (some-lazy-fn))

只会进行5递归调用以实现所需的元素。

4clojure问题的一个暂时的,远非完美的解决方案,它证明了这个想法:

(fn intercalate 
  [pred value col]
  (letfn [(looper [s head]
            (lazy-seq 
              (if-let [sec (first s)]
                (if (pred head sec)
                  (cons head (cons value (looper (rest s) sec)))
                  (cons head (looper (rest s) sec)))
               (if head [head] []))))]
    (looper (rest col) (first col))))

在那里,本地递归函数是looper,对于每个元素测试谓词是否为true,在这种情况下实现两个元素(添加交错的元素),否则只实现一个。

此外,您可以使用更高阶函数来避免递归

(fn [p v xs]
   (mapcat
    #(if (p %1 %2) [%1 v] [%1])
    xs
    (lazy-cat (rest xs) (take 1 xs))))

但正如@noisesmith在评论中所说,你只是调用一个调用lazy-seq的函数。