在clojure中使用lambda block-scanner

时间:2010-07-22 07:52:37

标签: clojure

我刚刚开始阅读Let over lambda,我想我会尝试在闭包章节中编写块扫描程序的clojure版本。

到目前为止,我有以下内容:

(defn block-scanner [trigger-string]
  (let [curr (ref trigger-string) trig trigger-string]
    (fn [data]
      (doseq [c data]
        (if (not (empty? @curr))
          (dosync(ref-set curr
           (if (= (first @curr) c)
             (rest @curr)
             trig)))))
      (empty? @curr))))
(def sc (block-scanner "jihad"))

我认为这是有效的,但我想知道我做得对,我能做得更好。

1 个答案:

答案 0 :(得分:8)

我不会使用ref-set而是使用alter,因为您不会将状态重置为全新值,而是将其更新为从旧值获取的新值。

(defn block-scanner
  [trigger-string]
  (let [curr (ref trigger-string)
        trig trigger-string]
    (fn [data]
      (doseq [c data]
        (when (seq @curr)
          (dosync
            (alter curr
                   #(if (-> % first (= c))
                      (rest %)
                      trig)))))
      (empty? @curr))))

然后没有必要使用引用,因为您不必协调更改。在这里,原子更适合,因为它可以在没有STM仪式的情况下进行更改。

(defn block-scanner
  [trigger-string]
  (let [curr (atom trigger-string)
        trig trigger-string]
    (fn [data]
      (doseq [c data]
        (when (seq @curr)
          (swap! curr
                 #(if (-> % first (= c))
                    (rest %)
                    trig))))
      (empty? @curr))))

接下来,我将摆脱势在必行的风格。

  • 它比它应该做的更多:它遍历所有数据 - 即使我们已经找到匹配。我们应该提早停止。
  • 它不是线程安全的,因为我们多次访问原子 - 它可能在两者之间发生变化。所以我们必须只触摸原子一次。 (虽然在这种情况下这可能不是很有趣,但是养成习惯是好的。)
  • 很难看。当我们得出结果时,我们可以在功能上完成所有工作并保存状态。
(defn block-scanner
  [trigger-string]
  (let [state    (atom trigger-string)
        advance  (fn [trigger d]
                   (when trigger
                     (condp = d
                       (first trigger)        (next trigger)
                       ; This is maybe a bug in the book. The book code
                       ; matches "foojihad", but not "jijihad".
                       (first trigger-string) (next trigger-string)
                       trigger-string)))
        update   (fn [trigger data]
                   (if-let [data (seq data)]
                     (when-let [trigger (advance trigger (first data))]
                       (recur trigger (rest data)))
                     trigger))]
    (fn [data]
      (nil? (swap! state update data)))))