2009-08-02 7 views
13

Sollte ichWas ist besser ?: (Reduzieren + ...) oder (Anwenden + ...)?

(apply + (filter prime? (range 1 20))) 

oder

(reduce + (filter prime? (range 1 20))) 

Edit: Dies ist die Quelle für die Prime in clojure von Toolkit zu optimieren.

(defn prime? [n] 
    (cond 
    (or (= n 2) (= n 3))   true 
    (or (divisible? n 2) (< n 2)) false 
    :else       
    (let [sqrt-n (Math/sqrt n)] 
     (loop [i 3] 
      (cond 
       (divisible? n i) false 
       (< sqrt-n i)  true 
       :else   (recur (+ i 2))))))) 
+0

möglich Duplikat von [Clojure: reduzieren vs. apply] (http://stackoverflow.com/questions/3153396/clojure-reduce-vs-apply). Die verknüpfte Frage ist neuer als diese, aber es hat IMO bessere Antworten, also nominiere ich es als den Überlebenden. – amalloy

Antwort

18

Wenn Sie in Bezug auf Leistung verlangen, ist die reduce besser durch ein wenig:

(time (dotimes [_ 1e6] (apply + (filter even? (range 1 20))))) 
"Elapsed time: 9059.251 msecs" 
nil 

(time (dotimes [_ 1e6] (reduce + (filter even? (range 1 20))))) 
"Elapsed time: 8420.323 msecs" 
nil 

über 7% Unterschied in diesem Fall, aber YMMV abhängig von der Maschine.

Sie haben Ihre Quelle für die prime?-Funktion nicht angegeben, daher habe ich even? als Prädikat angegeben. Denken Sie daran, dass Ihre Laufzeit von prime? dominiert werden kann, in diesem Fall die Wahl zwischen reduce und apply noch weniger zählt.

Wenn Sie fragen, was ist mehr "lispy" dann würde ich sagen, dass die reduce Implementierung vorzuziehen ist, als was Sie tun, ist eine Verringerung/Faltung im Sinne der funktionalen Programmierung.

13

Ich würde denken, dass reduce wäre vorzuziehen, wenn es verfügbar ist, weil apply verwendet die Liste als Argumente für die Funktion, aber wenn Sie eine große Anzahl - sagen, eine Million - Elemente in der Liste haben, werden Sie konstruiere einen Funktionsaufruf mit einer Million Argumenten! Das könnte bei einigen Lisp-Implementierungen zu Problemen führen.

+6

Common Lisp hat eine konstante CALL-ARGUMENTS-LIMIT. –

+2

Guter Punkt, obwohl kein Problem mit Clojure - Clojure ist glücklich mit dem Erstellen beliebig langer Argumentlisten auf diese Weise (sogar unendlich faul, wie es passiert ...) – mikera

8

Ich würde erwarten, eine faule Liste zu realisieren, die hässlich sein könnte, und Sie wollen nie davon ausgehen, dass Ihre Liste nicht faul ist, weil Sie plötzlich mit massivem Speicherverbrauch klatschen könnten.

Reduce wird sie nacheinander einsammeln und die Ergebnisse zu einem einzigen Ganzen zusammenfassen, ohne die ganze Liste auf einmal aufzunehmen.

+2

Apply in Clojure realisiert nicht wirklich die vollständige Argumentliste, es sei denn es benötigt zu. (apply (fn [& args] (nehmen Sie 5 Argumente)) (Bereich)) funktioniert gut zum Beispiel. – mikera

9

(reduzieren op ...) ist die Norm und (gelten op ...) die Ausnahme (vor allem für str und concat).

6

Ich werde Teufel Advokat spielen und für apply argumentieren.

reduce ist nehmen die clojure auf fold (genauer foldl), die linke-fach, und wird in der Regel mit einem Anfangselement definiert, da eine Falte Betrieb hat zwei Teile:

  • eine erste (oder " Null ") Wert

  • eine Operation zum Kombinieren von zwei Werten

so die Summe als finden et of numbers die natürliche Weise zu verwenden + ist wie (fold + 0 values) oder in clojure, (reduce + 0 values).

zeigt dies ausdrücklich das Ergebnis für eine leere Liste, was wichtig ist, weil es nicht mir klar ist, dass + kehrt 0 in diesem Fall - immerhin + ist ein Binäroperators (alle, dass fold Bedürfnisse oder annimmt) .

jetzt, in der Praxis stellt sich heraus, dass Clojure + als mehr als ein binärer Operator definiert ist. es wird viele oder sogar Nullwerte benötigen. cool. aber wenn wir diese "extra" Informationen verwenden, ist es freundlich, dies dem Leser zu signalisieren. (apply + values) tut dies - es sagt "ich benutze + auf seltsame Weise, als mehr als ein binärer Operator". und das hilft den Leuten (ich zumindest) den Code zu verstehen.

[es ist interessant zu fragen, warum apply fühlt sich klarer an. und ich denke, es ist teilweise, dass Sie dem Leser sagen: "Schau, + wurde entwickelt, um mehrere Werte zu akzeptieren (das ist, was gelten für verwendet wird), und so die Sprachimplementierung wird den Fall von Null-Werte enthalten." Dieses implizite Argument ist nicht vorhanden, wenn reduce auf eine einzige Liste angewendet wird.]

Alternativ ist (reduce + 0 values) auch in Ordnung. aber (reduce + values) löst eine instinktive Reaktion in mir aus: "huh, gibt + eine Null?".

und wenn Sie nicht einverstanden sind, dann, bitte, bevor Sie eine Antwort ablehnen oder posten, sind Sie sicher über was (reduce * values) wird für eine leere Liste zurückkehren?