首页 > 解决方案 > 为什么程序无休止地运行?

问题描述

为什么程序无休止地运行?

(defn lazycombine
([s] (lazycombine s []))
([s v] (let [a (take 1 s)
             b (drop 1 s)]
            (if (= a :start)
                (lazy-seq (lazycombine b v))
                (if (= a :end)
                    (lazy-seq (cons v (lazycombine b [])))
                    (lazy-seq (lazycombine b (conj v a))))))))

(def w '(:start 1 2 3 :end :start 7 7 :end))

(lazycombine w)

我需要一个函数,该函数通过从另一个形式为 [: start 1 2: end: start: 5: end] 的序列中获取元素并将以下所有元素组合到一个向量中,从而返回一个惰性元素序列

标签: clojure

解决方案


我会这样做,使用take-while

(ns tst.demo.core
  (:use tupelo.core tupelo.test))

(def data
  [:start 1 2 3 :end :start 7 7 :end])

(defn end-tag? [it] (= it :end))
(defn start-tag? [it] (= it :start))

(defn lazy-segments
  [data]
  (when-not (empty? data)
    (let [next-segment   (take-while #(not (end-tag? %)) data)
          data-next      (drop (inc (count next-segment)) data)
          segment-result (vec (remove #(start-tag? %) next-segment))]
      (cons segment-result
        (lazy-seq (lazy-segments data-next))))))

(dotest
  (println "result: " (lazy-segments data)))

运行我们得到:

result:  ([1 2 3] [7 7])

cons 使用(惰性或非惰性)递归构造序列时请注意合同。您必须返回序列中的下一个值,或者nil。提供niltocons与提供空序列相同:

 (cons 5 nil) => (5)
 (cons 5 [])  => (5)

因此使用when表格来测试终止条件很方便(而不是if在序列必须结束时使用并返回一个空向量)。

假设我们将 写成cons一个简单的递归:

  (cons segment-result
    (lazy-segments data-next))

这很好用并产生相同的结果。该部分唯一要做的lazy-seq就是延迟递归调用发生的时间。因为lazy-seq是 Clojure 内置的(特殊形式),所以它类似于loop/recur并且不像普通递归那样消耗堆栈。因此,我们可以在惰性序列中生成数百万(或更多)值,而无需创建StackOverflowError(在我的计算机上,默认的最大堆栈大小约为 4000)。考虑从 开始的整数的无限惰性序列0

(defn intrange
  [n]
  (cons n (lazy-seq (intrange (inc n)))))

(dotest
  (time
    (spyx (first (drop 1e6 (intrange 0))))))

删除前一百万个整数并获取下一个成功并且只需要几毫秒:

(first (drop 1000000.0 (intrange 0))) => 1000000
"Elapsed time: 49.5 msecs"

推荐阅读