2010-12-16 27 views
5

Estoy tratando de escribir una función de utilidad Clojure llamada map-longest (sugerencia de nombre alternativo apreciada). Esta función tendrá el siguiente "firma":Clojure map-longest

(map-longest fun missing-value-seq c1 & colls) 

y se comportan de manera similar a map, a excepción de lo que va a continuar procesando las colecciones suministrados hasta que el más larga se agota. Para las colecciones más cortas que las más largas, cuando se agoten los valores, las tomará del missing-values-seq. Debería ser flojo, pero obviamente no se puede usar con colecciones infinitas.

Ejemplo del uso:

(print (apply str 
    (map-longest #(str %1 \space %2 \space %3 \newline) (repeatedly "--") 
    ["a1" "a2" "a3"] ["b1" "b2"] ["c1" "c2" "c3" "c4"]))) 

Se debe producir el siguiente resultado:

a1 b1 c1 
a2 b2 c2 
a3 -- c3 
-- -- c4 

pero puede tener la llamada equivocada.

¿Cómo implemento esto? ¿La biblioteca clojure.core o clojure-contrib ya tiene algo como esto? Como alternativa al missing-value-seq, ¿sería mejor pasar una segunda función para generar los valores perdidos (por ejemplo: #(identity "--") en mi ejemplo)?

Caso de uso: Estoy escribiendo un pequeño texto spider solitario como un ejercicio para aprender Clojure/programación funcional. Necesito poder mostrar los tableaus del juego (tableaux para puristas :-)).

Respuesta

4

Aquí es una solución:

(defn map-longest 
    ([fn missing-value-fn c1] 
    (map fn c1)) 
    ([fn missing-value-fn c1 & colls] 
    (lazy-seq 
     (when (not-every? empty? (conj colls c1)) 
     (let [firsts (map first (conj colls c1))] 
      (cons 
      (apply fn (map #(if (nil? %) (missing-value-fn) %) firsts)) 
      (apply map-longest 
       (conj (map rest colls) (rest c1) missing-value-fn fn)))))))) 

prueba:

user=> (print (apply str 
     (map-longest #(str %1 \space %2 \space %3 \newline) #(identity "--") 
      ["a1" "a2" "a3"] ["b1" "b2"] ["c1" "c2" "c3" "c4"]))) 
a1 b1 c1 
a2 b2 c2 
a3 -- c3 
-- -- c4 
nil 

Tenga en cuenta que he tomado el enfoque missing-value-fn en lugar de la missing-value-seq uno.

actualización

Actualizado el código para cuidar el caso mencionado por ffriend en los comentarios.

prueba:

user=> (print (apply str 
      (map-longest #(str %1 \space %2 \space %3 \newline) #(identity "--") 
      ["a1" "a2" nil] ["b1" "b2"] ["c1" "c2" nil "c4"]))) 
a1 b1 c1 
a2 b2 c2 
-- -- -- 
-- -- c4 
nil 

Tenga en cuenta que esto va a reemplazar nil s en los Colls con el valor devuelto por el missing-value-fn.

+0

'(no-cada nil primicias??)' - que no funcionará para secuencias como [ 'c1' c2 nil 'c3]. – ffriend

+0

@ffriend: ¿puedes dar un código de prueba? –

+1

intente estas secuencias: ['a1' a2 nil] ['b1' b2] ['c1' c2 nil 'c4]. Puedes arreglarlo marcando '(cada? Empty? Cols)' antes de calcular 'primeros' y reemplazando calculando' primeros' por '(map first-or-val (conj colls c1))' (ver mi respuesta). – ffriend

1

Esto no es completamente correcto funcionamiento necesitas, pero un poco versión simplificada, por lo que podría conseguir el punto:

(defn first-or-val [col missing] 
    (if (empty? col) 
    missing 
    (first col))) 

(defn map-longest [f missing-value & cols] 
    (loop [cols cols, ret '()] 
    (cond (every? empty? cols) (reverse ret) 
      :else (recur (map rest cols) 
         (conj ret (apply f (map #(first-or-val % missing-value) 
               cols))))))) 

omití la pereza, y se puede añadir fácilmente con delay y force. También cambié missing-value-seq a solo missing-value - Creo que este no es el problema para que lo reemplace con secuencia o generador.

Ejemplo:

(print (apply str 
      (map-longest #(str %1 \space %2 \space %3 \newline) "--" 
         ['a1 'a2 'a3] ['b1 'b2] ['c1 'c2 'c3 'c4]))) 

Resultado:

a1 b1 c1 
a2 b2 c2 
a3 -- c3 
-- -- c4