2017-12-21 8 views
3

Ich versuche, ein Problem zu lösen 4clojure.com, wo ich die Anzahl der Elemente in einer Sammlung zählen soll, ohne count zu verwenden. Ich habe versucht, zwei Möglichkeiten mit for und let, die ich denke, sollte funktionieren, aber es scheint, dass die for-Schleife setzt die let zurückgesetzt.clojure für Funktion zurückgesetzt lassen

(#(for [x % :let [y 0]] (inc y)) [1 2 3 4 5]) 
;; which returns 
(1 1 1 1 1) 

(#(let [y 0] (for [x %] (inc y))) [1 2 3 4 5]) 
;; which returns 
(1 1 1 1 1) 

Also meine Frage ist, warum dies geschieht und wie ich meine „Variable“ erhalten in der Sammlung für jedes Element Inkrementieren zu halten. Wenn ich nur sage, dass die Variable "Wort" mich dazu bringt, mich zu fragen, ob ich versuche, etwas abzuschalten, was nicht möglich ist, fühle ich mich immer noch so, als würde das funktionieren.

+2

Eine Sache zu beachten, wie Sie clojure lernen, ist, dass 'for' kein Looping-Konstrukt ist - es ist ein Listenverständnis, um Listen zu erstellen.Wenn Sie also eine Schleife erstellen möchten, ist es normalerweise am besten, 'loop' zu verwenden. Rückblickend auf dieses habe ich 'reduce' verwendet, was neben der reinen Rekursion die" funktionalste Programmierung "ist, um das Problem anzugehen (mehr als eine explizite Schleife, meine ich). Prost und viel Spaß! – Josh

Antwort

9

in beiden Fällen nicht den Wert von y verändern können:

in ersten Fall for Wieder stellt die y auf jeder Schleife Schritt, das ist, warum Sie nicht, dass es Wert ändern könnte, auch wenn es wandelbar war .

der zweite Fall zeigt, dass der Wert wirklich unveränderbar ist, geben Sie (inc y) auf jedem Schritt, aber y ist immer Null. Einfaches Beispiel:

(let [x 10] 
    (inc x) 
    x) 

;;=> 10 

In der Regel diese Art von Aufgabe kann in der Regel mit den folgenden Ansätzen gelöst werden:

Erstens ist die einfache Rekursion:

(defn count-rec [data] 
    (if (seq data) 
    (inc (count-rec (rest data))) 
    0)) 

user> (count-rec [1 2 3 4 5]) 
;;=> 5 

es in einer Weise fehlerhaft ist, da es nicht Schwanz rekursiv ist und für große Sammlungen fehlschlagen würde

Der zweite ist clojure loop:

(defn count-loop [data] 
    (loop [res 0 data data] 
    (if (seq data) 
     (recur (inc res) (rest data)) 
     res))) 

user> (count-loop [1 2 3 4 5]) 
;;=> 5 

auch können Sie explizite Endrekursion verwenden, die sehr ähnlich sein würde:

(defn count-tailrec 
    ([data] (count-tailrec 0 data)) 
    ([c data] (if (seq data) 
       (recur (inc c) (rest data)) 
       c))) 

user> (count-tailrec [1 2 3 4 5]) 
;;=> 5 

Der dritte würde verwenden, um die reduce Funktion:

(defn count-reduce [data] 
    (reduce (fn [res _] (inc res)) 0 data)) 

user> (count-reduce [1 2 3 4 5]) 
;;=> 5 

Just for fun Sie auch diese verwenden könnte Art und Weise (ich würde es nicht raten, da es ein Overkill im Vergleich zu reduce ist):

(defn count-map [data] 
    (apply + (map (constantly 1) data))) 

user> (count-map [1 2 3 4 5]) 
;;=> 5 

Sie könnten auch clojure des wandelbaren Primitive verwenden, aber es ist nicht idiomatischen und sollte so lange vermieden werden, wie es sein könnte:

(defn count-atom [data] 
    (let [c (atom 0)] 
    (run! (fn [_] (swap! c inc)) data) 
    @c)) 

user> (count-atom [1 2 3 4 5]) 
;;=> 5 

und hier kommt den Cheat

[SPOILER ALARM!]

4clojure blockiert die Verwendung von count Funktion in dieser Aufgabe, aber nicht blockiert die .size Methode der Sammlungen des Java, so dass es mit #(.size (seq %))

gelöst werden kann
+0

Erstaunliche Antwort! Ich fand die Verwendung von 'reduce' und' loop/recur' am einfachsten, um mental für dieses spezifische Problem anzuwenden, aber die anderen Beispiele waren wirklich hilfreich, um besser zu verstehen, wie man das Problem auf verschiedene Arten betrachtet. – maxorcist

+0

@Thumbnail: was meinst du? es gibt diese genaue Variante in der Antwort (siehe "count-reduce") – leetwinski

+0

@leetwinski hat es verpasst. – Thumbnail