2009-08-02 20 views
13

debo usar¿Qué es mejor ?: (reducir + ...) o (aplicar + ...)?

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

o

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

Editar: Esta es la fuente de clojure privilegiada en la optimización de caja de herramientas.

(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

posible duplicado de [Clojure: reducir vs. aplicar] (http://stackoverflow.com/questions/3153396/clojure-reduce-vs-apply). La pregunta vinculada es más nueva que esta, pero tiene mejores respuestas de la OMI, por lo que la nomino como superviviente. – amalloy

Respuesta

18

Si solicita en términos de rendimiento, la reduce es mejor por un poco:

(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 

diferencia Alrededor del 7% en este caso, pero tu caso es distinto dependiendo de la máquina.

No ha proporcionado su fuente para la función prime?, por lo que he sustituido even? como el predicado. Tenga en cuenta que su tiempo de ejecución puede estar dominado por prime?, en cuyo caso la elección entre reduce y apply es aún menor.

Si está preguntando qué es más "lispy", entonces diría que la implementación reduce es preferible, ya que lo que está haciendo es reducir/doblar en el sentido de programación funcional.

13

Creo que sería preferible reduce cuando esté disponible, porque apply usa la lista como argumentos para la función, pero cuando hay un gran número -por ejemplo, un millón- de elementos en la lista, lo hará ¡construye una llamada a función con un millón de argumentos! Eso podría causar algunos problemas con algunas implementaciones de Lisp.

+6

Common Lisp tiene una constante CALL-ARGUMENTS-LIMIT. –

+2

Buen punto, aunque no es un problema con Clojure - Clojure está contento con la construcción de listas de argumentos arbitrariamente largas de esta manera (incluso infinitos perezosos, como ocurre ...) – mikera

8

Esperaría aplicar para realizar una lista perezosa que podría ser fea, y nunca querrá suponer que su lista no es floja, porque de repente podría encontrarse golpeado con un uso masivo de memoria.

Reduce va a agarrarlos 1 por uno y junta los resultados en un solo conjunto, sin tomar toda la lista a la vez.

+2

Aplicar en Clojure no se da cuenta de la lista completa de argumentos a menos que sea necesario a. (apply (fn [& args] (take 5 args)) (rango)) funciona bien, por ejemplo. – mikera

9

(reducir op ...) es la norma y (aplicar op ...) la excepción (especialmente para str y concat).

6

voy a jugar al abogado del diablo y abogar por apply.

reduce es tomar de clojure en fold (más exactamente foldl), la izquierda veces, y se define generalmente con un elemento inicial, porque una operación de pliegue tiene dos partes:

  • una inicial (o " cero ") valor

  • una operación para combinar dos valores

manera de encontrar la suma de tan de números, la forma natural de usar + es como (fold + 0 values) o, en clojure, (reduce + 0 values).

esta muestra de manera explícita el resultado de una lista vacía, lo cual es importante porque no es obvio para mí que + vuelve 0 en este caso - después de todo, + es un operador binario (todo lo que fold necesidades o asumido) .

ahora, en la práctica, resulta que + de clojure se define como más que un operador binario. tomará muchos o incluso valores cero. guay. pero si estamos usando esta información "adicional", es amistoso señalar eso al lector. (apply + values) hace esto - dice "estoy usando + de una manera extraña, como más que un operador binario". y eso ayuda a las personas (yo, al menos) a entender el código.

[es interesante preguntar por qué apply se siente más claro. y creo que es en parte por lo que le está diciendo al lector: "mira, + fue diseñado para aceptar valores múltiples (para eso se usa la aplicación), por lo que la implementación del lenguaje incluirá el caso de valores cero". ese argumento implícito no está presente con reduce aplicado a una sola lista.]

alternativamente, (reduce + 0 values) también está bien. pero (reduce + values) desencadena una reacción instintiva en mí: "¿eh, + proporciona un cero?".

y si no está de acuerdo, por favor, antes de Downvote o una respuesta, ¿estás seguro de (reduce * values) sobre lo volverá a una lista vacía?

Cuestiones relacionadas