clojure“看 - 说”顺序

时间:2017-07-04 07:08:35

标签: clojure

处理一些2015 AoC问题以学习clojure ......以下对于第40次迭代来说足够快,但在此之后又停止了很多。我与其他一些人的解决方案进行了比较,对我来说,为什么这么慢,这并不是很明显。我试图使用recur相信它与循环一样高效(并避免堆栈消耗)。我不是100%明确的一件事是,如果仅仅使用复发与使用循环复发之间存在明显差异。我对它进行了两种方式的测试,并没有发现任何差异。

(def data "3113322113")

(defn encode-string [data results count]
   (let [prev (first data)
         curr (second data)]
     (cond (empty? data) results
           (not= prev curr)
           (recur (rest data) (str results count prev) 1)
           :else (recur (rest data) results (inc count)))))

(count
 (nth (iterate #(encode-string % "" 1) data) 40 #_50))

我所反对的解决方案的一个例子是布鲁斯·豪曼的,这非常好:

(defn count-encode [x]
  (apply str
         (mapcat 
          (juxt count first)
          (partition-by identity x))))

我意识到在我的解决方案中我反复迭代非常大的字符串,但是我没有看到Bruce的速度如此快,因为虽然他没有明确地迭代,但分区可能在幕后迭代。

1 个答案:

答案 0 :(得分:7)

您的版本正在计算类似

的内容
(str "11" (str "22" (str "31" ...)))

为每两个字符构建一个全新的String对象。由于这涉及在每一步中迭代输入和输出字符串中的每个字符,因此您的操作在字符串的长度上是二次的。

您要比较的解决方案是不同的:它构建了一个懒惰的整数序列,这是一个线性时间过程。然后,它会像

那样
(apply str [1 1 2 2 3 1])

相同
(str 1 1 2 2 3 1 ...)

str,当使用多个参数调用时,使用StringBuilder以增量方式有效地构建结果,如果在每个中间步骤都需要完整的String对象,则该优化不可用。结果,整个过程是线性时间,而不是二次方。