Изменение поведения карты в Clojure - PullRequest
6 голосов
/ 27 января 2012

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

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

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

Я написал для этого функцию, но она кажется не очень расширяемой с помощью 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)))))

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

Ответы [ 6 ]

7 голосов
/ 27 января 2012

Ваш метод краткий, но неэффективный (он вызывает 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 до произвольной арности.

6 голосов
/ 27 января 2012

Еще один ленивый вариант, который можно использовать с произвольным числом входных последовательностей:

(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)
4 голосов
/ 27 января 2012

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

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

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

1 голос
/ 28 января 2012

Как насчет этого:

(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 голосов
/ 22 мая 2015

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

(+) ;=> 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

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

0 голосов
/ 21 мая 2015

В соответствии с решением @ LeNsTR , но проще и быстрее:

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...