2016-06-16 7 views
1

Ich möchte eine Funktion schreiben, um die Summe aller durch 3 teilbaren Zahlen in einer gegebenen Liste zurückzugeben. Ich weiß nicht, wie man auf die Größe einer Liste passt. Ich bekomme eine Null-Zeiger-Ausnahme, wenn ich den folgenden Code ausführe.Wie kann ich die Mustergröße in clojure anpassen?

(defn sum3 [coll] 
    (reduce (fn [sum el] 
      (if (zero? (mod el 3)) 
       (+ sum el) 
       sum)) 0 coll)) 

Beachten Sie die Verwendung von zero?: mit reduce

Ich versuche, dies mit ersten Prinzipien zu tun, statt loopmap und reduce mit

(defn sum3 
    [[head & tail]] 
    (if (= (mod head 3) 0) 
    (+ head (sum3 tail)) 
    (sum3 tail)) 
    [[head]] 
    (if (= (mod head 3) 0) 
    head 
    0)) 


(defn -main 
    "I don't do a whole lot ... yet." 
    [& args] 
    (println (sum3 [1 2 3]))) 

Antwort

1

Funktion Versand in Clojure auf der Anzahl der Argumente der Funktion und nicht auf die Anzahl der destrukturierten erfolgt Werte im Funktionsaufruf.

Sobald die entsprechende Arity ausgewählt wurde und die Funktion gestartet wurde, tritt die Destrukturierung auf und bindet die Symbole an die destrukturierten Werte.

Fortunatly clojure bietet beliebige benutzerdefinierte Funktion Versand in Form von multimethods so, wenn Sie basierend auf der Länge der Argumente versenden möchten, können Sie. Für Ihr Beispiel ist es zwar übertrieben, aber nicht besonders schwer. Es gibt andere Fälle, in denen es Sinn macht.

Eine normale, einzelne arity, Annäherung an diese Funktion würde wie folgt aussehen:

user> (defn sum3 
     [[head & tail]] 
     (if (seq tail) 
     (if (= (mod head 3) 0) 
      (+ head (sum3 tail)) 
      (sum3 tail)) 
     (if (= (mod head 3) 0) 
      head 
      0))) 
#'user/sum3 
user> (sum3 [1 2 3 4 5 6]) 
9 

Sie sollten immer im allgemeinen wiederholen verwenden, anstatt direkte Rekursion obwohl für diese Frage ist es nicht zu viel von einem Problem, wenn demonstriert dieses Prinzip.

+1

'(sum3())' ist ein Problem für diese Implementierung. – amalloy

0

Zuerst ist die Version tun lassen. Keine Mustererkennung erforderlich.

Dann können wir gehen auf Rekursivität verwenden:

(defn sum3 
    ([coll] (sum3 0 coll)) 
    ([sum [head & tail]] 
    (let [add (if (zero? (mod head 3)) 
       (+ sum head) 
       sum)] 
    (if tail 
     (recur add tail) 
     add)))) 

Wieder passende kein Muster (Sie würden core.match für diese -clojure verwenden müssen keine Musterabgleich verwendet werden). Der Versand erfolgt nur mit der Anzahl der Argumente. Aus diesem Grund war Ihre Funktion keine gültige Syntax: Sie war in beiden Fällen eine 1-arity-Funktion. Beachten Sie die Verwendung von recur, um die Funktion zu überprüfen. Es kann nur für die Tail-Rekursion verwendet werden.

Wir könnten es machen eine 1-arity Funktion ohne auch auf Argumente Dispatching:

(defn sum3 
    [[head & tail]] 
    (let [m3? (zero? (mod head 3))] 
    (if (seq tail) 
     (if m3? 
     (+ head (sum3 tail)) 
     (sum3 tail)) 
     (if m3? head 0)))) 

Beachten Sie, dass diese Version nicht Schwanz-rekursiv ist und für große Eingänge überlaufen.

Aber wirklich die reduce Version - oder sogar die loop Version viel besser aussehen.

0

in der Tat ist der Hauptfehler in der defn Syntax. Du definierst hier nicht zwei Aritäten, sondern machst eine Arität [[head]] und gehst dann den Körper, was absolut gültiger Clojure-Code ist. Um 2 arities sollten Sie jeweils in Klammern gesetzt:

(defn sum3 
    ([[head]] 
    (if (= (mod head 3) 0) 
    head 
    0)) 
    ([[head & tail]] 
    (if (= (mod head 3) 0) 
    (+ head (sum3 tail)) 
    (sum3 tail)))) 

aber es wäre auch scheitern, weil man nicht anders arities definiert, sind sie gleich (ein Argument Anruf), so würde der Compiler erzeugt einen Fehler. Die Clojure-Destrukturierung ist überhaupt kein Mustervergleich. Es ist nur eine Möglichkeit, Elemente aus einer Sammlung abzurufen. Aber Sie können dieses Problem beheben (Art Länge Musterabgleich emulieren) mit einer geringfügigen Änderung:

user> 
(defn sum3 
    ([head] 
    (if (= (mod head 3) 0) 
    head 
    0)) 
    ([head & tail] 
    (if (= (mod head 3) 0) 
    (+ head (apply sum3 tail)) 
    (apply sum3 tail)))) 
#'user/sum3 

user> (sum3 1 2 3 4 5 6) 
9 

user> (apply sum3 [1 2 3 4 5 6]) 
9 

jetzt Sie fair Variante mit zwei verschiedenen arities haben.

Aber ja, es ist nicht Schwanz rekursiv, so im wirklichen Leben Sie mit reduce oder loop/recur gehen würde