在clojure中使用lambda block-scanner

Lew*_*ubb 3 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"))
Run Code Online (Sandbox Code Playgroud)

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

kot*_*rak 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))))
Run Code Online (Sandbox Code Playgroud)

然后没有必要使用refs,因为您不必协调更改.在这里,原子更适合,因为它可以在没有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))))
Run Code Online (Sandbox Code Playgroud)

接下来我会摆脱命令式的风格.

  • 它做得比它应该做的更多:它遍历所有数据 - 即使我们已经找到匹配.我们应该提早停止.
  • 它不是线程安全的,因为我们多次访问原子 - 它可能在两者之间发生变化.所以我们必须只触摸原子一次.(虽然在这种情况下这可能不是很有趣,但是养成这种习惯是好的.)
  • 它很丑.当我们得出结果时,我们可以在功能上完成所有工作并保存状态.
(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)))))
Run Code Online (Sandbox Code Playgroud)