2012-01-27 24 views
6

Necesito modificar el comportamiento de la función del mapa para proporcionar la asignación no con un tamaño mínimo de colección sino con un máximo y usar cero para los elementos faltantes.Cambiando el comportamiento del mapa en Clojure

comportamiento estándar:

(map + [1 2 3] [4 5 6 7 8]) => [5 7 9] 

comportamiento necesario:

(map + [1 2 3] [4 5 6 7 8]) => [5 7 9 7 8] 

escribí función para hacer esto, pero no parece muy extensible con varargs.

(defn map-ext [f coll1 coll2] 
    (let [mx (max (count coll1) (count coll2))] 
    (map f 
    (concat coll1 (repeat (- mx (count coll1)) 0)) 
    (concat coll2 (repeat (- mx (count coll2)) 0))))) 

¿Hay una manera mejor de hacer esto?

Respuesta

6

Otra variante perezoso, utilizable con un número arbitrario de secuencias de entrada:

(defn map-ext [f ext & seqs] 
    (lazy-seq 
    (if (some seq seqs) 
    (cons (apply f (map #(if (seq %) (first %) ext) seqs)) 
      (apply map-ext f ext (map rest seqs))) 
    ()))) 

Uso:

user> (map-ext + 0 [1 2 3] [4 5 6 7 8]) 
(5 7 9 7 8) 

user> (map-ext + 0 [1 2 3] [4 5 6 7 8] [3 4]) 
(8 11 9 7 8) 
+0

He publicado una mejora putativa [aquí] (http://stackoverflow.com/a/30387354/1562315). – Thumbnail

4

Si lo que desea que funcione para cualquier número de colecciones, intente:

(defn map-ext [f & colls] 
    (let [mx (apply max (map count colls))] 
     (apply map f (map #(concat % (repeat (- mx (count %)) 0)) colls)))) 

Clojure> (map-ext + [1 2] [1 2 3] [1 2 3 4]) 
(3 6 6 4) 

Sospecho que puede haber mejores soluciones, aunque (como sugiere Trevor Caira, esta solución no es perezoso debido a las llamadas para contar).

7

Su método es conciso, pero ineficiente (llama a count). Una solución más eficiente, que no requiere la totalidad de sus secuencias de entrada para ser almacenados en la memoria sigue:

(defn map-pad [f pad & colls] 
    (lazy-seq 
    (let [seqs (map seq colls)] 
    (when (some identity seqs) 
     (cons (apply f (map #(or (first %) pad) seqs)) 
      (apply map-pad f pad (map rest seqs))))))) 

utilizado como este:

user=> (map-pad + 0 [] [1] [1 1] (range 1 10)) 
(3 3 3 4 5 6 7 8 9) 

Editar: generalizada map-pad a aridad arbitraria.

+0

Agradable, pero solo acepta 2 colecciones. Realmente queremos la descendencia de nuestras dos respuestas :) –

+0

Sí, la pereza persistente es un buen enfoque, gracias – mishadoff

+1

Tenga en cuenta que el uso de '(map # (o (first%) pad) seqs)' significa que cualquier 'nil's and' falso's en los 'seqs' será reemplazado por' pad'. –

1

¿Qué hay de que:

(defn map-ext [f x & xs] 
    (let [colls (cons x xs) 
     res (apply map f colls) 
     next (filter not-empty (map #(drop (count res) %) colls))] 
    (if (empty? next) res 
     (lazy-seq (concat res (apply map-ext f next)))))) 

user> (map-ext + [1 2 3] [4] [5 6] [7 8 9 10]) 
(17 16 12 10) 
0

lo largo de las líneas de @LeNsTR's solution, pero más simple y más rápido:

(defn map-ext [f & colls] 
    (lazy-seq 
    (let [colls (filter seq colls) 
     firsts (map first colls) 
     rests (map rest colls)] 
    (when (seq colls) 
     (cons (apply f firsts) (apply map-ext f rests)))))) 

(map-ext + [1 2 3] [4] [5 6] [7 8 9 10]) 
;(17 16 12 10) 

he acabo de dar cuenta de Michał solución aceptada Marczyk, que es superior: se trata adecuadamente con las funciones de mapeo asimétricas como -.

0

Podemos hacer que Michał Marczyk's answer sea más limpio usando la convención - que muchas funciones básicas siguen - que obtienes un valor predeterminado o de identidad llamando a la función sin argumentos. Para ejemplos:

(+) ;=> 0 
(concat) ;=>() 

El código se convierte en

(defn map-ext [f & seqs] 
    (lazy-seq 
    (when (some seq seqs) 
    (cons (apply f (map #(if (seq %) (first %) (f)) seqs)) 
      (apply map-ext f (map rest seqs))) 
    ))) 

(map-ext + [1 2 3] [4 5 6 7 8] [3 4]) 
;(8 11 9 7 8) 

he hecho los cambios mínimos. Se podría acelerar un poco.

Es posible que tengamos una función que inyectará un valor predeterminado tal en una función que carece de ella:

(defn with-default [f default] 
    (fn 
    ([] default) 
    ([& args] (apply f args)))) 

((with-default + 6)) ;=> 6 
((with-default + 6) 7 8) ;=> 15 

Esto podría acelerarse o incluso se convirtió en una macro.

Cuestiones relacionadas