2010-07-22 4 views
3

Je viens de commencer à lire Let over lambda et j'ai pensé que j'essaierais d'écrire une version clojure du bloc-scanner dans le chapitre sur les fermetures.Let over lambda bloc-scanner en clojure

Je donne les résultats suivants jusqu'à présent:

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

Cela fonctionne, je pense, mais je voudrais savoir ce que je faisais bien et ce que je pouvais faire mieux.

Répondre

8

Je n'utiliserais pas ref-set mais alter parce que vous ne réinitialisez pas l'état à une toute nouvelle valeur, mais le mettez à jour à une nouvelle valeur qui est obtenue à partir de l'ancien.

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

Ensuite, il n'est pas nécessaire d'utiliser ref car vous n'avez pas besoin de coordonner les modifications. Ici un atome est un meilleur ajustement, puisqu'il peut être changé sans toute la cérémonie 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)))) 

Ensuite, je voudrais me débarrasser du style impératif.

  • il fait plus que ce qu'il devrait: il traverse toutes les données - même si nous avons déjà trouvé une correspondance. Nous devrions arrêter tôt.
  • il n'est pas thread-safe, car nous accédons à l'atome plusieurs fois - il peut changer entre les deux. Nous devons donc toucher l'atome une seule fois. (Bien que ce n'est probablement pas intéressant dans ce cas, mais il est bon de faire une habitude.)
  • c'est moche. Nous pouvons faire tout le travail fonctionnellement et juste sauver l'état, quand nous arrivons à un résultat.
(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

Wow, bonnes choses maintenant je dois digérer et comprendre. Merci. –