Clojure中的Let over lambda块扫描器

3

我刚开始阅读《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个回答

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))))

如果您不需要协调更改,那么就不需要使用引用(refs)。在这种情况下,原子(atom)是更好的选择,因为它可以在没有所有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)))))

哇,好东西,现在我得消化并理解它。谢谢。 - Lewis Jubb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接