2012-01-27 2 views
6

Мне нужно изменить поведение функции карты, чтобы обеспечить сопоставление не с минимальным размером коллекции, а с максимальным значением и использование нуля для отсутствующих элементов.Изменение поведения карты в Clojure

Стандартное поведение:

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

Необходимое поведение:

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

Я написал функцию, чтобы сделать это, но это, кажется, не очень расширяемая с переменным числом аргументов.

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

Есть ли лучший способ сделать это?

ответ

6

Другой ленивым вариант, пригодный для использования с произвольным числом входных последовательностей:

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

Использование:

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

Я опубликовал предполагаемое улучшение [здесь] (http://stackoverflow.com/a/30387354/1562315). – Thumbnail

4

Если вы просто хотите, чтобы работать для любого числа коллекций, попробуйте:

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

Я подозреваю, что может быть лучше решения, хотя (как предполагает Тревор Caira, это решение не лень из-за звонков считать).

7

Ваш метод является кратким, но неэффективным (он вызывает count). Более эффективное решение, которое не требует полноты его входных последовательностей, которые будут сохранены в памяти следующим образом:

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

использован как это:

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

Редактировать: Обобщенных map-pad произвольной арности.

+0

Приятный, но он принимает только 2 коллекции. Мы действительно хотим отпрыска наших двух ответов :) –

+0

Да, сохраняющаяся лень - хороший подход, спасибо – mishadoff

+1

Обратите внимание, что использование '(map # (или (first%) pad) seqs)' означает, что любые 'nil' и ' false 'в' seqs' будут заменены на 'pad'. –

1

Как о том, что:

(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

Вдоль линий @LeNsTR's solution, но проще и быстрее:

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

Я только заметил, принятое решение Михала Marczyk, которая превосходит: она имеет дело должным образом с асимметричными отображающими функциями, такими как -.

0

Мы можем сделать Michał Marczyk's answer neater, используя соглашение, в котором много основных функций, - вы получаете значение по умолчанию или идентификатор, вызывая функцию без аргументов. Для примера:

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

код становится

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

Я сделал минимальные изменения. Его можно немного ускорить.

Нам может понадобиться функция, которая будет придать такое значение по умолчанию в функцию, которая испытывает недостаток его:

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

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

Это может быть ускорен или даже превратился в макрос.

Смежные вопросы