2010-07-22 24 views
3

我剛剛開始閱讀Let over lambda,我想我會嘗試在閉包章節中編寫塊掃描程序的clojure版本。在clojure上使用lambda塊掃描器

我見到目前爲止以下內容:

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

這工作我想,但我想知道我做得對,什麼我可以做的更好。

回答

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,因爲您不必協調更改。這裏原子更合適,因爲它可以在沒有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))))) 
+0

哇,好東西,現在我必須消化這一點,並理解它。謝謝。 – 2010-07-22 13:53:38