How do I pass var args to an anonymous function in Clojure? - clojure

Is it possible to do var args for an anonymous function in Clojure?
For example, how do I turn:
(#(reduce + (map * %1 %2 %3)) [1 2 3] [4 5 6] [7 8 9])
into something like,
(#(reduce + (map * [& args])) [1 2 3] [4 5 6] [7 8 9])

This solves the problem:
user> ((fn[& args] (reduce + (apply map * args))) [1 2 3] [4 5 6] [7 8 9])
270
or
user> (#(reduce + (apply map * %&)) [1 2 3] [4 5 6] [7 8 9])
270

Use the apply function
I'm not aware of a way to do this with the #(...) syntax, but here is your example using fn
((fn [& args] (reduce + (apply map * args))) [1 2 3] [4 5 6] [7 8 9])
You can use %& to get the rest argument in the #(...) form, resulting in
(#(reduce + (apply map * %&)) [1 2 3] [4 5 6] [7 8 9])

Related

Clojure - Applying a Function to a vector of vectors

I have a vector [[[1 2] [3 4]] [[5 6] [7 8]] [9 10] 11]. I want to apply a function to this vector but keep the data structure.
For example I want to add 1 to every number but keep the data structure to get the result being [[[2 3] [4 5]] [[6 7] [8 9]] [10 11] 12]. Is this possible?
I have tried
(map #(+ 1 %) (flatten [[[1 2] [3 4]] [[5 6] [7 8]] [9 10] 11]))
=> (2 3 4 5 6 7 8 9 10 11 12)
But you can see that the data structure is not the same.
Is there maybe a function that takes (2 3 4 5 6 7 8 9 10 11 12) to [[[2 3] [4 5]] [[6 7] [8 9]] [10 11] 12]
I thought maybe to use postwalk but I'm not sure if this is correct.
Any help would be much appreciated
You can use postwalk:
(require '[clojure.walk :as walk])
(let [t [[[1 2] [3 4]] [[5 6] [7 8]] [9 10] 11]]
(walk/postwalk (fn [x] (if (number? x) (inc x) x)) t))
also the classic recursive solution is not much more difficult:
(defn inc-rec [data]
(mapv #((if (vector? %) inc-rec inc) %) data))
#'user/inc-rec
user> (inc-rec [1 [2 3 [4 5] [6 7]] [[8 9] 10]])
;;=> [2 [3 4 [5 6] [7 8]] [[9 10] 11]]
Another way to solve your problem is via Specter. You do need another dependency then, but it can be a helpful library.
(ns your-ns.core
(:require [com.rpl.specter :as specter]))
(def data [[[1 2] [3 4]] [[5 6] [7 8]] [9 10] 11])
(specter/defprotocolpath TreeWalker) ;; define path walker
(specter/extend-protocolpath TreeWalker
;; stop walking on leafs (in this case long)
Object nil
;; when we are dealing with a vector, TreeWalk all elements
clojure.lang.PersistentVector [specter/ALL TreeWalker])
You can extend it to perform more complicated operations. For this use case normal Clojure is good enough.
(specter/transform [TreeWalker] inc data)
;; => [[[2 3] [4 5]] [[6 7] [8 9]] [10 11] 12]

Return all elements from a list

I have a function
(defn my-fn [a b & args]
[a
(for [arg args]
(into [] (butlast arg)))
b])
If I do (my-fn [1 2] [3 4] [5 6 2] [7 8 3])
It returns [[1 2] ([5 6] [7 8]) [3 4]]
I want the output to be [[1 2] [5 6] [7 8] [3 4]] but I cannot figure out how to do this
Any help would be much appreciated.
I'd into [a] all your mapped values and then conj b at the end. E.g.
(defn my-fn [a b & args]
(-> [a]
(into (map (comp vec butlast) args))
(conj b)))
and one more variant, using quote and unquote splicing:
user> (defn my-fn2 [a b & args]
`[~a ~#(map (comp vec butlast) args) ~b])
;;=> #'user/my-fn2
user> (my-fn2 [1 2] [3 4] [5 6 2] [7 8 3])
;;=> [[1 2] [5 6] [7 8] [3 4]]
Your for statement returns a list.
(defn my-fn [a b & args]
(->> [[a]
(map (comp vec butlast) args)
[b]]
(apply concat)
vec))
The final vec transform your seq into a vector.
Here is one solution:
(defn my-fn [a b & args]
(vec
(concat
[a]
(for [arg args]
(into [] (butlast arg)))
[b])))
(my-fn [1 2] [3 4] [5 6 2] [7 8 3])
=> [[1 2] [5 6] [7 8] [3 4]]
Please note that concat returns a lazy list, which for larger and/or more complicated problems can cause a StackOverflowException. The outer vec converts the lazy list in to a concrete vector. It is unnecessary for this example and you could remove it if you want.
As another note, concat expects each argument to be a list/vector, so I have wrapped a and b into 1-element vectors. The output of for is already a (lazy) list.
Because conj, cons, concat, etc can be somewhat non-intuitive in their behavior, you may wish to checkout the helper functions glue, append, prepend, & friends in the Tupelo library.
Background
Suppose we have a mixture of scalars & vectors (or lists) that we want to combine into a single vector. We want a function ??? to give us the following result:
(??? 1 2 3 [4 5 6] 7 8 9) => [1 2 3 4 5 6 7 8 9]
Clojure doesn’t have a function for this. Instead we need to wrap all of the scalars into vectors and then use glue or concat:
; can wrap individually or in groups
(glue [1 2 3] [4 5 6] [7 8 9]) => [1 2 3 4 5 6 7 8 9] ; could also use concat
(glue [1] [2] [3] [4 5 6] [7] [8] [9]) => [1 2 3 4 5 6 7 8 9] ; could also use concat
It may be inconvenient to always wrap the scalar values into vectors just to combine them with an occasional vector value. Instead, it might be more convenient to unwrap the vector values, then combine the result with other scalars. We can do that with the ->vector and unwrap functions:
(->vector 1 2 3 4 5 6 7 8 9) => [1 2 3 4 5 6 7 8 9]
(->vector 1 (unwrap [2 3 4 5 6 7 8]) 9) => [1 2 3 4 5 6 7 8 9]
It will also work recursively for nested unwrap calls:
(->vector 1 (unwrap [2 3 (unwrap [4 5 6]) 7 8]) 9) => [1 2 3 4 5 6 7 8 9]
Alternate Solution Using Python-Style Generator Functions
You could also solve it this way:
(ns tst.clj.core
(:use clj.core tupelo.test)
(:require [tupelo.core :as t] ))
(t/refer-tupelo)
(defn my-fn [a b & args]
(lazy-gen
(yield a)
(yield-all (for [arg args]
(into [] (butlast arg))))
(yield b)))
(my-fn [1 2] [3 4] [5 6 2] [7 8 3]) => ([1 2] [5 6] [7 8] [3 4])
Here lazy-gen creates a context for a "generator function", where successive values are added to the output list using the yield and yield-all functions. Note that the yield-all function is like the Clojure "unquote-splicing" operator ~# and "unwraps" its sequence into the output stream.
An even easier answer is to dive deeper into the for part of your original solution to apply the yield output tap:
(defn my-fn2 [a b & args]
(lazy-gen
(yield a)
(doseq [arg args]
(yield (butlast arg)))
(yield b)))
(my-fn2 [1 2] [3 4] [5 6 2] [7 8 3]) => ([1 2] (5 6) (7 8) [3 4])
This version of the solution follows your original logic more closely,
and avoids the accidental complexity of the for function wrapping its results into a sequence, which conflicts with the a and b values not being wrapped in a vector. We also no longer need the (into [] ...) part of the original function.
Note that we have replaced the for with doseq since:
We are no longer using the return value of the loop
And, therefore, a lazy solution would never run

How to swap 2 elements in Vector of Vector

How do I extend this:
What is the idiomatic way to swap two elements in a vector
to essentially a 2D array?
[[1 2 3] [4 5 6] [7 8 9]] --> [[1 2 5] [4 3 6] [7 8 9]]
Use get-in and assoc-in:
(defn swap-in [vv p1 p2]
(let [v1 (get-in vv p1)
v2 (get-in vv p2)
(-> vv
(assoc-in p1 v2)
(assoc-in p2 v1))))
So now:
(def my-vec [[1 2 3] [4 5 6] [7 8 9]])
(swap-in my-vec [0 2] [1 1])
=> [[1 2 5] [4 3 6] [7 8 9]]
This will work with vectors nested to any "depth", so long as the specified positions in my-vec actually exist.

Clojure: map map

I would like to use the map function on map. But I can't get it to work.
A toy example:
(map map [+ - *] [1 2 3] [4 5 6] [7 8 9])
I expect a result like (12 15 18) but all I get is an error.
Thanks.
As an alternative to already existing answers, you could replace the outer map with a list comprehension, which is more readable than a nested map IMHO:
user=> (defn fun [ops & args]
#_=> (for [op ops]
#_=> (apply map op args)))
#'user/fun
user=> (fun [+ - *] [1 2 3] [4 5 6] [7 8 9])
((12 15 18) (-10 -11 -12) (28 80 162))
If you want to map each of the operator separately over the lists, then use
((fn [ops & args] (map #(apply map %1 args) ops)) [+ - *] [1 2 3] [4 5 6] [7 8 9])
or if you are willing to reorder arguments
(map #(map %1 [1 2 3] [4 5 6] [7 8 9]) [+ - *])
Both give the result of ((12 15 18) (-10 -11 -12) (28 80 162))
You can use juxt:
(apply map list (map (juxt + - *) [1 2 3] [4 5 6] [7 8 9]))
Which will result in: ((12 15 18) (-10 -11 -12) (28 80 162))

Distributing list items to variables in clojure

When I pass this function
(into []
(map #(+ %1 %2)
[1 2]
[5 6]))
I get this result: [6 8]
What should I do to get this: [6 7 7 8] while keeping this #(+ %1 %2) ?
Seems like map isn't the right function in this case.
Use for when you want a Cartesian product:
user=> (for [x [1 2] y [5 6]]
#_=> (+ x y))
(6 7 7 8)
for is one option as Alex answer shows. map can also be used (with mapcat) as shown below:
user=> (mapcat #(map (partial + %1) [5 6]) [1 2])
(6 7 7 8)