Distributing list items to variables in clojure - 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)

Related

generalising nested calls to map with a function

I'm looking for a good way to define function map-nest that perform nested mapping operations that can be customised by an extra nesting value:
(map-nest 0 inc [1 2 3 4 5])
; 0th LEVEL
; expands to (map inc [1 2 3 4 5])
=> '(2 3 4 5 6)
(map-nest 1 inc [[1 2 3] [4 5]])
; 1st LEVEL
; expands to (map (fn [x] (map inc x)) [[1 2 3] [4 5]])
=> '((2 3 4) (5 6))
(map-nest 2 inc [[[1 2] [3 4]] [[5]]])
; 2nd LEVEL
; expands to
; (map (fn [y] (map (fn [x] (map inc x)) y))
; [[[1 2] [3 4]] [[5]]])
=> '(((2 3) (4 5)) ((6)))
; and so on
;
is there a good way to write this?
The simplest way would be some recursive mapping function, maybe something like this:
(defn map-nest [level f data]
(let [f (if (zero? level) f (partial map-nest (dec level) f))]
(map f data)))
user> (map-nest 0 inc [1 2 3 4 5])
;;=> (2 3 4 5 6)
user> (map-nest 1 inc [[1 2 3] [4 5]])
;;=> ((2 3 4) (5 6))
user> (map-nest 2 inc [[[1 2] [3 4]] [[5]]])
;;=> (((2 3) (4 5)) ((6)))
You could also think of nested mappings as a reductions stream, where each starting from the function, producing the one next level mapping on every iteration:
(defn make-nested-mappers [f]
(rest (iterate #(partial map %) f)))
(defn map-nest [level f data]
((nth (make-nested-mappers f) level) data))

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

Clojure; select all nth element from list of lists with unequal size, for n = 1, 2,

I'd like to have a function, such that,
(f '([1 4 7] [2 5 9] [3 6]))
would give
([1 2 3] [4 5 6] [7 9])
I tried
(apply map vector '([1 4 7] [2 5 9] [3 6]))
would only produce:
([1 2 3] [4 5 6])
I find it hard to describe my requirements that it's difficult for me to search for a ready solution.
Please help me either to improve my description, or pointer to a solution.
Thanks in advance!
I'd solve a more general problem which means you might reuse that function in the future. I'd change map so that it keeps going past the smallest map.
(defn map-all
"Like map but if given multiple collections will call the function f
with as many arguments as there are elements still left."
([f] (map f))
([f coll] (map f coll))
([f c1 & colls]
(let [step (fn step [cs]
(lazy-seq
(let [ss (keep seq cs)]
(when (seq ss)
(cons (map first ss)
(step (map rest ss)))))))]
(map #(apply f %) (step (conj colls c1))))))
(apply map-all vector '([1 4 7] [2 5 9] [3 6]))
(apply map-all vector '([1 false 7] [nil 5 9] [3 6] [8]))
Note, that as opposed to many other solutions, this one works fine even if any of the sequences contain nil or false.
or this way with loop/recur:
user> (defn transpose-all-2 [colls]
(loop [colls colls res []]
(if-let [colls (seq (filter seq colls))]
(recur (doall (map next colls))
(conj res (mapv first colls)))
res)))
#'user/transpose-all-2
user> (transpose-all-2 x)
[[1 2 3] [4 5 6] [7 9]]
user> (transpose-all-2 '((0 1 2 3) (4 5 6 7) (8 9)))
[[0 4 8] [1 5 9] [2 6] [3 7]]
If you know the maximum length of the vectors ahead of time, you could define
(defn tx [colls]
(lazy-seq
(cons (filterv identity (map first colls))
(tx (map rest colls)))))
then
(take 3 (tx '([1 4 7] [2 5 9] [3 6])))
A simple solution is
(defn transpose-all
[colls]
(lazy-seq
(let [ss (keep seq colls)]
(when (seq ss)
(cons (map first ss) (transpose-all (map rest ss)))))))
For example,
(transpose-all '([1 4 7] [2 5 9] [3 6] [11 12 13 14]))
;((1 2 3 11) (4 5 6 12) (7 9 13) (14))
Here is my own attempt:
(defn f [l]
(let [max-count (apply max (map count l))
l-patched (map (fn [e] (if (< (count e) max-count)
(concat e (take (- max-count (count e)) (repeat nil)))
e)) l)]
(map (fn [x] (filter identity x)) (apply map vector l-patched))
))
Another simple solution:
(->> jagged-list
(map #(concat % (repeat nil)))
(apply map vector)
(take-while (partial some identity)))
A jagged-list like this
'([1 4 7 ]
[2 5 9 ]
[3 6 ]
[11 12 13 14])
will produce:
'([1 2 3 11]
[4 5 6 12]
[7 9 nil 13]
[nil nil nil 14])
Here is another go that doesn't require you to know the vector length in advance:
(defn padzip [& [colls]]
(loop [acc [] colls colls]
(if (every? empty? colls) acc
(recur (conj acc (filterv some?
(map first colls))) (map rest colls)))))

How to write a Clojure function that returns a list of adjacent pairs?

I'm trying to write a function adjacents that returns a vector of a sequence's adjacent pairs. So (adjacents [1 2 3]) would return [[1 2] [2 3]].
(defn adjacents [s]
(loop [[a b :as remaining] s
acc []]
(if (empty? b)
acc
(recur (rest remaining) (conj acc (vector a b))))))
My current implementation works for sequences of strings but with integers or characters the REPL outputs this error:
IllegalArgumentException Don't know how to create ISeq from: java.lang.Long clojure.lang.RT.seqFrom (RT.java:494)
The problem here is in the first evaluation loop of (adjacents [1 2 3]), a is bound to 1 and b to 2. Then you ask if b is empty?. But empty? works on sequences and b is not a sequence, it is a Long, namely 2. The predicate you could use for this case here is nil?:
user=> (defn adjacents [s]
#_=> (loop [[a b :as remaining] s acc []]
#_=> (if (nil? b)
#_=> acc
#_=> (recur (rest remaining) (conj acc (vector a b))))))
#'user/adjacents
user=> (adjacents [1 2 3 4 5])
[[1 2] [2 3] [3 4] [4 5]]
But, as #amalloy points out, this may fail to give the desired result if you have legitimate nils in your data:
user=> (adjacents [1 2 nil 4 5])
[[1 2]]
See his comment for suggested implementation using lists.
Note that Clojure's partition can be used to do this work without the perils of defining your own:
user=> (partition 2 1 [1 2 3 4 5])
((1 2) (2 3) (3 4) (4 5))
user=> (partition 2 1 [1 2 nil 4 5])
((1 2) (2 nil) (nil 4) (4 5))
Here is my short answer. Everything becomes a vector, but it works for all sequences.
(defn adjacent-pairs [s]
{:pre [(sequential? s)]}
(map vector (butlast s) (rest s)))
Testing:
user=> (defn adjacent-pairs [s] (map vector (butlast s) (rest s)))
#'user/adjacent-pairs
user=> (adjacent-pairs '(1 2 3 4 5 6))
([1 2] [2 3] [3 4] [4 5] [5 6])
user=> (adjacent-pairs [1 2 3 4 5 6])
([1 2] [2 3] [3 4] [4 5] [5 6])
user=>
This answer is probably less efficient than the one using partition above, however.

Clojure: Semi-Flattening a nested Sequence

I have a list with embedded lists of vectors, which looks like:
(([1 2]) ([3 4] [5 6]) ([7 8]))
Which I know is not ideal to work with. I'd like to flatten this to ([1 2] [3 4] [5 6] [7 8]).
flatten doesn't work: it gives me (1 2 3 4 5 6 7 8).
How do I do this? I figure I need to create a new list based on the contents of each list item, not the items, and it's this part I can't find out how to do from the docs.
If you only want to flatten it one level you can use concat
(apply concat '(([1 2]) ([3 4] [5 6]) ([7 8])))
=> ([1 2] [3 4] [5 6] [7 8])
To turn a list-of-lists into a single list containing the elements of every sub-list, you want apply concat as nickik suggests.
However, there's usually a better solution: don't produce the list-of-lists to begin with! For example, let's imagine you have a function called get-names-for which takes a symbol and returns a list of all the cool things you could call that symbol:
(get-names-for '+) => (plus add cross junction)
If you want to get all the names for some list of symbols, you might try
(map get-names-for '[+ /])
=> ((plus add cross junction) (slash divide stroke))
But this leads to the problem you were having. You could glue them together with an apply concat, but better would be to use mapcat instead of map to begin with:
(mapcat get-names-for '[+ /])
=> (plus add cross junction slash divide stroke)
The code for flatten is fairly short:
(defn flatten
[x]
(filter (complement sequential?)
(rest (tree-seq sequential? seq x))))
It uses tree-seq to walk through the data structure and return a sequence of the atoms. Since we want all the bottom-level sequences, we could modify it like this:
(defn almost-flatten
[x]
(filter #(and (sequential? %) (not-any? sequential? %))
(rest (tree-seq #(and (sequential? %) (some sequential? %)) seq x))))
so we return all the sequences that don't contain sequences.
Also you may found useful this general 1 level flatten function I found on clojuremvc:
(defn flatten-1
"Flattens only the first level of a given sequence, e.g. [[1 2][3]] becomes
[1 2 3], but [[1 [2]] [3]] becomes [1 [2] 3]."
[seq]
(if (or (not (seqable? seq)) (nil? seq))
seq ; if seq is nil or not a sequence, don't do anything
(loop [acc [] [elt & others] seq]
(if (nil? elt) acc
(recur
(if (seqable? elt)
(apply conj acc elt) ; if elt is a sequence, add each element of elt
(conj acc elt)) ; if elt is not a sequence, add elt itself
others)))))
Example:
(flatten-1 (([1 2]) ([3 4] [5 6]) ([7 8])))
=>[[1 2] [3 4] [5 6] [7 8]]
concat exampe surely do job for you, but this flatten-1 is also allowing non seq elements inside a collection:
(flatten-1 '(1 2 ([3 4] [5 6]) ([7 8])))
=>[1 2 [3 4] [5 6] [7 8]]
;whereas
(apply concat '(1 2 ([3 4] [5 6]) ([7 8])))
=> java.lang.IllegalArgumentException:
Don't know how to create ISeq from: java.lang.Integer
Here's a function that will flatten down to the sequence level, regardless of uneven nesting:
(fn flt [s] (mapcat #(if (every? coll? %) (flt %) (list %)) s))
So if your original sequence was:
'(([1 2]) (([3 4]) ((([5 6])))) ([7 8]))
You'd still get the same result:
([1 2] [3 4] [5 6] [7 8])