Is this poor clojure? - clojure

I have data in the following structure (simplified):
(def m {"a" [1 2 3] "b" [4 5 6] "c" [7 8 9] "d" [10 11 12]})
I have a recursive function and at any iteration I am interested in the i'th element of each of a, b, c and d.
What I have been doing is:
(loop [i 0]
(let [a ((m "a") i)
b ((m "b") i)
c ((m "c") i)
d ((m "d") i)]
...do stuff with a, b, c and d...
In other words I am creating bindings for a, b, c and d and that is because I would rather not repeat something like ((m "a") i) multiple times in my code every time I need this value.
This seems a little clunky and not a very functional style. Is there a better way to achieve this? That is, either a more elegant way of creating the bindings or perhaps even a way that avoids bindings?
Edit: Adding an explanation as to why I need loop and not map:
My data represents a tree and my function traverses the tree to find the appropriate terminal node. The i'th element of each of the vectors is the data related to the i'th node. Therefore I start with i = 0 as this is the root node. I do some logic and this tells me which node to go to next. The logic is the same at each node and this is why I have used loop and recur. In reality there are perhaps 200 nodes and so one route through the tree could be 0 > 6 > 45 > 67 > 123 > 130 > 156 > done.
I would be hugely impressed if there is a way to traverse a tree with map and not loop.

Depending what your bigger problem to solve is, this may help:
user> (def m {"a" [1 2 3] "b" [4 5 6] "c" [7 8 9] "d" [10 11 12]})
#'user/m
user> (defn each-element [a b c d] (vector a b c d))
#'user/each-element
user> (apply map each-element (map m ["a" "b" "c" "d"]))
([1 4 7 10] [2 5 8 11] [3 6 9 12])
Where you would replace the definition of each-element with your function that operates one element each from "a", "b", "c", and "d".

It looks like what you really want to do is make a new map with the same keys and all their values transformed, which doesn't need an index i, or any bindings:
e.g. increment all the map's values
(into {} (map (fn [[k v]] [k (map inc v)]) {"a" [1 2 3] "b" [4 5 6] "c" [7 8 9] "d" [10 11 12]}))
=> {"a" (2 3 4), "b" (5 6 7), "c" (8 9 10), "d" (11 12 13)}
(inc could be any function to transform the values)
another way to say that is:
(into {} (map (juxt key (comp (partial map inc) val)) {"a" [1 2 3] "b" [4 5 6] "c" [7 8 9] "d" [10 11 12]}))
and here we didn't need to bind anything! juxt, compandpartial build our transform for us
another approach is:
(reduce (fn [r [k v]] (assoc-in r [k] (map inc v))) {} {"a" [1 2 3] "b" [4 5 6] "c" [7 8 9] "d" [10 11 12]})
where r is the result we are building, and again k,v are the key and value of the each map entry in the input map, which the reduce function receives as vectors which we're destructuring with [k v]
In your code it looks like you want to slice through all lists at once, which we can do thus:
(apply (partial map list) [[1 2 3] [4 5 6] [7 8 9]])
=> ((1 4 7) (2 5 8) (3 6 9))
which gives us a sequence of all the first elements, all the second etc...
(just as an aside, we could do that in general:
(apply (partial map list) (take 7 (partition 3 (iterate inc 0))))
=> ((0 3 6 9 12 15 18) (1 4 7 10 13 16 19) (2 5 8 11 14 17 20))
without any need for indexing :-)
anyway, then you could do something with those:
(map (partial reduce +) (apply (partial map list) [[1 2 3] [4 5 6] [7 8 9]]))
=> (12 15 18)
which is a separate matter from transforming maps into new maps, but anyway.
In general, in Clojure one hardly ever needs to index things, and often (as you suspected) code is clearer without binding things. Play around with map, reduce, apply, juxt, partial and comp and everything will become easier

To make your code more expressive, ...
extract the data for the node you're interested in into a map and
use destructuring to make the functions that deal with it
easier to write (and to read).
The following function extracts the map for node node-no from your data:
(defn node-data [data node-no]
(into {} (map (fn [[k v]] [k (get v node-no)]) data)))
For example ...
(node-data m 1)
; {"a" 2, "b" 5, "c" 8, "d" 11}
And here's an example of a function using destructuring to produce local arguments bound to the values associated with the corresponding string keys:
(defn do-whatever [{:strs [a b c d]}] [a b c d (- (+ a b) (+ c d))])
(do-whatever (node-data m 1))
; [2 5 8 11 -12]
You've seen the like in an answer to your separate question.
If you want to convert all your data at once, the following function transposes it into a vector of node maps :
(defn transpose-map [a]
(let [transpose (fn [s] (apply map vector s))
ta (transpose a)
tsa (transpose (second ta))]
(mapv #(zipmap (first ta) %) tsa)))
(transpose-map m)
; [{"d" 10, "c" 7, "b" 4, "a" 1} {"d" 11, "c" 8, "b" 5, "a" 2}
; {"d" 12, "c" 9, "b" 6, "a" 3}]
Notes
You probably do better to use loop than anything else. Almost all of Clojure's repetitive abstractions deal with sequences: map, reduce, iterate ... . Since your computation is a simple loop, they are of no use to you. You could dress it up using iterate and reduce (with reduced), but there seems no point.
The above assumes that the tree is unaltered throughout. If you are altering the tree as you navigate it, you had better look at zippers.
P.S. Are the string keys natural identifiers? If not, prefer keywords.

Instead of binding each of a, b, c and d separately in your let binding, you could do it in a more concise way using destructuring:
(loop [i 0]
(let [[a b c d] (map (fn [[ltr nums]] (nums i)) m)]
; ... do stuff with a, b, c & d ...
For this to work, you would need to use a sorted-map, in order to preserve the order of a, b, c and d in your map:
(def m (sorted-map "a" [1 2 3], "b" [4 5 6], "c" [7 8 9], "d" [10 11 12]))
This is still not very functional because you're still using a loop... there is probably a more functional approach, but it would depend on what exactly you're trying to do with this map of data.

You can perhaps read this article of Alex Miller on Tree Traversal in Clojure : http://www.ibm.com/developerworks/library/j-treevisit/index.html

Related

Clojure iteration over vector of functions

I am reading a book on Clojure that says:
"Another fun thing you can do with map is pass it a collection of functions. You could use this if you wanted to perform a set of calculations on different collections of numbers, like so:"
(def sum #(reduce + %))
(def avg #(/ (sum %) (count %)))
(defn stats
[numbers]
(map #(% numbers) [sum count avg]))
(stats [3 4 10])
; => (17 3 17/3)
(stats [80 1 44 13 6])
; => (144 5 144/5)
"In this example, the stats function iterates over a vector of functions, applying each function to numbers."
I find this very confusing and the book doesn't give anymore explanation.
I know % represent arguments in anonymous functions, but I can't work out what values they represent in this example. What are the %'s?
And also how can stats iterate over count if count is nested within avg?
Many thanks.
It helps to not think in "code being executed" , but in "expression trees being reduced". Expression trees are rewritten until the result appears. Symbols are replaced by "what they stand for" and functions are applied to their arguments when a "live function" appears in the first position of a list; as in (some-function a b c). This is done in top-down fashion from the top of the expression tree to the leaves, stopping when the quote symbol is encountered.
In the example below, we unfortunately cannot mark what has already been reduced and what not as there is no support for coloring. Note that the order of reduction is not necessarily the one corresponding to what the compiled code issued by the Clojure compiler actually would do.
Starting with:
(defn stats
[numbers]
(map #(% numbers) [sum count avg]))
...we shall call stats.
First difficulty is that stats can be called with a collection as a single thing:
(stats [a0 a1 a2 ... an])
or it could be called with a series of values:
(stats a0 a1 a2 ... an)
Which is it? Unfortunately the expected calling style can only be found by looking at the function definition. In this case, the definition says
(defn stats [numbers] ...
which means stats expects a single thing called numbers. Thus we call it like this:
(stats [3 4 10])
Now reduction starts! The vector of numbers that is the argument is reduced to itself because every element of a vector is reduced and a number reduces to itself. The symbol stats is reduced to the function declared earlier. The definition of stats is actually:
(fn [numbers] (map #(% numbers) [sum count avg]))
...which is a bit hidden by the defn shorthand. Thus
(stats [3 4 10])
becomes
((fn [numbers] (map #(% numbers) [sum count avg])) [3 4 10])
Next, reducing the fn expression yields a live function of one argument. Let's mark the live function with a ★ and let's use mathematical arrow notation:
(★(numbers ➜ (map #(% numbers) [sum count avg])) [3 4 10])
The live function is on first position of the list, so a function call will follow. The function call consists in replacing the occurrence of numbers by the argument [3 4 10] in the live function's body and stripping the outer parentheses of the whol expression:
(map #(% [3 4 10]) [sum count avg])
Symbols map, sum, count, avg resolve to known, defined functions, where map and count come from the Clojure core library, and the rest has been defined earlier. Again, we mark them as live:
(★map #(% [3 4 10]) [★sum ★count ★avg]))
Again, the # % notation is a shorthand for a function taking one argument and inserting it into the % position, let's make this evident:
(★map (fn [x] (x [3 4 10])) [★sum ★count ★avg]))
Reducing the fn expression yields a live function of one argument. Again, mark with ★ and let's use mathematical arrow notation:
(★map ★(x ➜ (x [3 4 10])) [★sum ★count ★avg]))
A live function ★map is in head position and thus the whole expression is reduced according to the specification of map: apply the first argument, a function, to every element of the 2nd argument, a collection. We can assume the collection is created first, and then the collection members are further evaluated, so:
[(★(x ➜ (x [3 4 10])) ★sum)
(★(x ➜ (x [3 4 10])) ★count)
(★(x ➜ (x [3 4 10])) ★avg)]
Every element of the collection can be further reduced as each has a live function of 1 argument in head position and one argument available. Thus in each case, x is appropriately substituted:
[(★sum [3 4 10])
(★count [3 4 10])
(★avg [3 4 10])]
Every element of the collection can be further reduced as each has a live function of 1 argument in head position. The exercise continues:
[ ((fn [x] (reduce + x)) [3 4 10])
(★count [3 4 10])
((fn [x] (/ (sum x) (count x))) [3 4 10])]
then
[ (★(x ➜ (reduce + x)) [3 4 10])
3
(★(x ➜ (/ (sum x) (count x))) [3 4 10])]
then
[ (reduce + [3 4 10])
3
(/ ((fn [x] (reduce + x)) [3 4 10]) (count [3 4 10]))]
then
[ (★reduce ★+ [3 4 10])
3
(/ (*(x ➜ (reduce + x)) [3 4 10]) (count [3 4 10]))]
then
[ (★+ (★+ 3 4) 10)
3
(/ (reduce + [3 4 10]) (count [3 4 10]))]
then
[ (★+ 7 10)
3
(★/ (★reduce ★+ [3 4 10]) (★count [3 4 10]))]
then
[ 17
3
(★/ 17 3)]
finally
[ 17
3
17/3]
You can also use function juxt. Try (doc juxt) on the REPL:
clojure.core/juxt
([f] [f g] [f g h] [f g h & fs])
Takes a set of functions and returns a fn that is the juxtaposition
of those fns. The returned fn takes a variable number of args, and
returns a vector containing the result of applying each fn to the
args (left-to-right).
((juxt a b c) x) => [(a x) (b x) (c x)]
Let's try that!
(def sum #(reduce + %))
(def avg #(/ (sum %) (count %)))
((juxt sum count avg) [3 4 10])
;=> [17 3 17/3]
((juxt sum count avg) [80 1 44 13 6])
;=> [144 5 144/5]
And thus we can define stats alternatively as
(defn stats [numbers] ((juxt sum count avg) numbers))
(stats [3 4 10])
;=> [17 3 17/3]
(stats [80 1 44 13 6])
;=> [144 5 144/5]
P.S.
Sometimes Clojure-code is hard to read because you don't know what "stuff" you are dealing with. There is no special syntactic marker for scalars, collections, or functions and indeed a collection can appear as a function, or a scalar can be a collection. Compare with Perl, which has notation $scalar, #collection, %hashmap, function but also $reference-to-stuff and $$scalarly-dereferenced-stuff and #$collectionly-dereferenced-stuff and %$hashmapply-dereferenced-stuff).
% stands for the first argument of the anonymous function.
(map #(% numbers) [sum count avg]))
Is equivalent to the following:
(map (fn [f] (f numbers)) [sum count avg])
where I have used the regular version rather than the short form version for anonymous functions and explicitly named the argument as 'f". See https://practicalli.github.io/clojure/defining-behaviour-with-functions/anonymous-functions.html for a fuller explanation of short form version.
In Clojure functions are first-class citizens so they can be treated as values and passed to functions. When functions are passed as values this is called generating higher-order functions (see https://clojure.org/guides/higher_order_functions).

Read list of numbers by place value? (ClojureScript tricks)

I was wondering today if i have a list of numbers. Is there a function in ClojureScript which returns a number made from the numbers by place value? (I dont mean a list(), but just some numbers)
For e.g.:
I have a some numbers 1, 2, 3, and i am looking for a function which converts it to 123 (one hundred and twenty-three).
I thought I found a solution for this with read-string
(read-string (str 1 2 3))
;=> 123
but sadly it not works well if i want to use it in the function discussed below.
I have a "functionmade" map where keys are vectors and values are numbers. Like this:
{[0 0] 0, [0 1] 1, ... [4 5] 45, ... [9 9] 99}
The only way i can use get function on this map is:
(get {[0 2] 2} [0 2])
;=> 2
; or
(get {[4 5] 45} [4 5])
;=> 45
This is grinding, but it works. To make it look much better i tried to define a function where i used read-string: (read/ refers to cljs.reader)
(defn get-oxo [x y]
(get {[x y] (read/read-string (str x y))} [x y]))
;So i can use it like this for e.g.:
(get-oxo 4 5)
;=> 45
It also returns values which are not in the map:
(get-oxo 112 358)
;=> 112358
I suppose the problem is that this way get-oxo returns not the value of the paired key, but the number constructed from x y...
So the questions is how can I fix it to return the value of the paired key and not the number constructed from the numbers i give to the functions?
(There is another problem if i want to generate a map with wider range of numbers, for e.g. not just 0-9 but 0-99. This way the algorythm of get-oxo is not true. I use the function discussed at: ClojureScript zipmap tricks me or what?).
the problem is you get what you put:
just decompose your function to see it clearly:
(defn get-oxo [x y]
(let [new-map {[x y] (read/read-string (str x y))}]
(println new-map)
(get new-map [x y])))
#'user/get-oxo
user> (get-oxo 100 200)
;;=> {[100 200] 100200}
;;=> 100200
So you generate your map inside the function, and get it's key
and you get... a totally valid behavior
to get something from your map , you need to have your map as the first argument of get function
(defn get-oxo [input x y]
(get input [x y]))
#'user/get-oxo
user> (def data {[0 0] 0 [0 1] 1 [0 2] 2})
#'user/data
user> (get-oxo data 0 1)
1
user> (get-oxo data 2 3)
nil
then, if you need the key (i mean pair from map) instead of value, you can modify it this way, using find function instead of get:
(defn get-oxo [input x y]
(first (find input [x y])))
#'user/get-oxo
user> (get-oxo data 0 1)
[0 1]
user> (get-oxo data 2 3)
nil
also writing and reading string to get a number looks redundant. You can easily make a simple function for that:
(defn digits->num [digits]
(reduce (fn [acc d] (+ (* 10 acc) d))
0
digits))
#'user/digits->num
user> (digits->num [0])
0
user> (digits->num [])
0
user> (digits->num [1])
1
user> (digits->num [1 2 3 4])
1234
user> (digits->num [0 0 1 0])
10
You can simply apply str function to your sequence of digits:
(apply str '(1 2 3))
;; => "123"

Putting singles and lists into a list in clojure

Is there a more elegant way to have into work with single items and lists than the following (admittedly atrocious) function?
(defn into-1-or-more
[this-list list-or-single]
(into this-list (flatten (conj [] list-or-single))))
Which can handle either:
(into-1-or-more [1 2 3 4] 5)
;[1 2 3 4 5]
Or:
(into-1-or-more [1 2 3 4] [5 6 7 8])
;[1 2 3 4 5 6 7 8]
I am building a collection with reduce using into [] with results from functions in a list. However some of the functions return single items, and others return lists of items. Like:
(reduce #(into [] (% data)) [func-return-item func-return-list func-return-either])
Would the best solution be to just do the following instead?
(into [] (flatten (map #(% data) [func-return-item ...])
Although it would be more ideal to know for sure what return type you are getting, here is a simple answer:
(flatten [ curr-list (mystery-fn) ] )
Examples:
(flatten [[1 2 3] 9 ] )
;=> (1 2 3 9)
(flatten [[[1] 2 3] [4 5] 6 ] )
;=> (1 2 3 4 5 6)
You could wrap it into a function if you want, but it hardly seems necessary.
This transducer flattens sequential inputs, but only by one "level":
(defn maybe-cat [rf]
(let [catrf (cat rf)]
(fn
([] (rf))
([result] (rf result))
([result input]
(if (sequential? input)
(catrf result input)
(rf result input))))))
Example:
(into [] maybe-cat [[:foo] :bar [[:quux]]])
;= [:foo :bar [:quux]]
As this example demonstrates, this approach makes it possible to include sequential collections in the output (by wrapping them in an additional sequential layer – [[:quux]] produces [:quux] in the output).

Clojure map two argument

I am trying to map a function that takes two arguments to a vector.
For example, given a vector [2, 3, 4], I'd like to add 2 to every digits by "+" function.
Here is what I tried :
(map (defn add2[i] (+ 2 i)) [2, 3, 4])
(map (+ 2) [2, 3, 4])
Anonymous functions are declared with fn or #() :
(map (fn [i] (+ 2 i)) [2 3 4])
(map #(+ 2 %) [2 3 4])
You could also use partial :
(map (partial + 2) [2 3 4])
Commas are optional and usually not used in source code.
You want to use the anonymous function fn
$ (map (fn [x] (+ 2 x)) [2 3 4])
=> [4 5 6]
#(do %1 %2) is reader sugar for fn
$ (map #(+ %) [2 3 4]);the first argument can be either % or %1
=> [4 5 6]
or alternatively you can use partial to return a function that partially applies the function you give it to one of the arguments. You should prefer this one for partial application of constant values.
$ (map (partial + 2) [2 3 4]);the first argument can be either % or %1
=> [4 5 6]
(map (fn [i] (+ 2 i)) [1 2 3])
Or instead use the following anonymous function :#(+ 2 %)
You can also do:(partial + 2) to define a partial function
Your second code example won't work because you need to tell clojure where to put the argument that map is applying the function to. The way to do this is to either define a function (as you've done in your first example) or to create an anonymous function as I've done above.

How to split an input sequence according to the input number given

I'm writing a clojure function like:
(defn area [n locs]
(let [a1 (first locs)]
(vrp (rest locs))))
I basically want to input like: (area 3 '([1 2] [3 5] [3 1] [4 2])) But when I do that it gives me an error saying Wrong number of args (1) passed. But I'm passing two arguments.
What I actually want to do with this function is that whatever value of n is inputted (say 3 is inputted), then a1 should store [1 2], a2 should store [3 5], a3 should store ([3 1] [4 2]). What should I add in the function to get that?
clojue's build in split-at function is very close to solving this. It splits a sequence "at" a given point. So if we first split the data apart and then wrap the second half in a list and concatenate it back together again it should solve this problem:
user> (let [[start & end] (split-at 2 sample-data)]
(concat start end))
([1 2] [3 5] ([3 1] [4 2]))
the & before end in let causes the last item to be rolled up in a list. It's equivalent to:
user> (let [[start end] (split-at 2 sample-data)]
(concat start (list end)))
([1 2] [3 5] ([3 1] [4 2]))
If I recklessly assume you have some function called vrp that needs data in this form then you could finish your function with something like this:
(defn area [n locs]
(let [[start end] (split-at (dec n) sample-data)
a (concat start (list end))]
(apply vrp a)))
Though please forgive me to making wild guesses as to the nature of vrp, I could be totally off base here.