2012-01-27 18 views
6

Potrzebuję zmodyfikować zachowanie funkcji mapy, aby zapewnić odwzorowanie nie przy minimalnym rozmiarze kolekcji, ale z maksymalną wartością i używać zera dla brakujących elementów.Zmiana zachowania mapy w Clojure

standardowe zachowanie:

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

Potrzebne zachowanie:

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

napisałem funkcję, aby to zrobić, ale nie wydaje się bardzo rozciągliwe z 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))))) 

Czy jest lepszy sposób to zrobić?

Odpowiedz

6

Kolejny leniwy wariant, nadające z dowolnej liczby sekwencji wejściowych:

(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))) 
    ()))) 

Zastosowanie:

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

Wysłałem domniemane ulepszenie [tutaj] (http://stackoverflow.com/a/30387354/1562315). – Thumbnail

4

Jeśli chcesz po prostu go do pracy dla dowolnej liczby zbiorów, spróbuj:

(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) 

Podejrzewam, że może być lepsze rozwiązania chociaż (jak sugeruje Trevor Caira, to rozwiązanie nie jest leniwy, ze względu na wezwania liczyć).

7

Twoja metoda jest zwięzła, ale nieskuteczna (wywołuje count). Bardziej wydajne rozwiązanie, które nie wymaga całokształt jego sekwencji wejściowych mają być przechowywane w pamięci w następujący sposób:

(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))))))) 

Używane tak:

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

Edit: Uogólnione map-pad arbitralnej liczbę operandów.

+0

Przyjemnie, ale akceptuje tylko 2 kolekcje. Naprawdę chcemy potomstwa naszych dwóch odpowiedzi :) –

+0

Tak, uporczywe lenistwo jest dobrym podejściem, dzięki – mishadoff

+1

Pamiętaj, że używając '(map # (lub (first%) pad) seqs)' oznacza, że ​​każdy 'nil's i' fałsz w 'seqs' zostanie zamieniony na' pad'. –

1

Jak o tym:

(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

Wzdłuż linii @LeNsTR's solution, ale prostsze i szybsze:

(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) 

Właśnie zauważyłem Michał Marczyk zaakceptował rozwiązanie, które jest lepszy: dotyczy on właściwie z asymetrycznych funkcji mapowania takich jak -.

0

Możemy sprawić, by Michał Marczyk's answer stał się bardziej popularny, stosując konwencję - która z wielu podstawowych funkcji jest następująca - aby uzyskać domyślną lub tożsamą wartość, wywołując funkcję bez żadnych argumentów. Przykłady:

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

Kod staje

(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) 

Zrobiłem minimalne zmiany. Można trochę przyspieszyć.

Możemy potrzebować funkcję, która będzie wstrzyknąć taką wartość domyślną do funkcji, której brakuje go:

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

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

ten może być przyspieszony lub nawet zamienił makro.

Powiązane problemy