Changing Map Behavior in Clojure

I need to change the behavior of the map function so that it does not map to the minimum size of the collection, but to the maximum value and use zero for missing elements.

Standard behavior:

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

Necessary behavior:

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

I wrote a function for this, but it is not very extensible with 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))))) 

Is there a better way to do this?

+6
source share
6 answers

Another lazy option that can be used with an arbitrary number of input sequences:

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

Using:

 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) 
+6
source

Your method is concise but inefficient (it calls count ). A more efficient solution that does not require the complete input of input sequences, which must be stored in memory, is the following:

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

Used as follows:

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

Edit: Generalized map-pad to arbitrary arity.

+7
source

If you just want it to work for any number of collections, try:

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

I suspect there may be better solutions, though (as Trevor Kaira suggests, this solution is not lazy because of the challenges to count).

+4
source

How about this:

 (defn map-ext [fx & 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) 
+1
source

The @LeNsTR lines have a solution , but simpler and faster:

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

I just noticed that Michaล‚ Marczyk made a decision that is superior: it correctly applies to asymmetric display functions such as - .

0
source

We can make Michaล‚ Marczyk answer neater using an agreement in which there are many main functions - you get the default value or identifier, calling the function without arguments, Examples:

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

Code becomes

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

I have made minimal changes. It can be accelerated a little.

We may need a function that will introduce such a default value into a function that does not have this.

 (defn with-default [f default] (fn ([] default) ([& args] (apply f args)))) ((with-default + 6)) ;=> 6 ((with-default + 6) 7 8) ;=> 15 

It can be accelerated or even turned into a macro.

0
source

Source: https://habr.com/ru/post/907019/


All Articles