Getting ith element of a sequence in Clojure - clojure

I am currently learning clojure and I need help.
Say if I have below which is p,
{:variable "x", :coef (2 5 7 13)}
I can access coef by doing (last (last p)) and can iterate range of it by doing
(for [i (range(count (last (last p))))]
i
)
Now, I want to access each ith coef elements. For example if i is 0, then I want to get 2, if i is 3, then I want to get 13).

Using the same definition of p you currently have:
user=> (def p {:variable "x", :coef '(2 5 7 13)})
#'user/p
user=> (defn nth-coef [p i] (nth (get p :coef) i))
#'user/nth-coef
user=> (nth-coef p 0)
2
user=> (nth-coef p 1)
5
If p had a vector in the coefficients, you could use get-in:
user=> (def p {:variable "x", :coef [2 5 7 13]})
#'user/p
user=> (get-in p [:coef 1])
5

Related

Map only selected elements in a list

Suppose I have a list of elements L, a function g, and a list of indices I.
Then, how can I map the function g only to the elements of the list L specified by the indices I?
For instance, if g is the squaring function, L is the list (1 2 3 4 5 6 7) and I is the set of indices (1 3 4), then I should obtain
(1 4 3 16 25 6 7), that is the list L in which I squared the elements in positions I.
(The first index is 0, like it is used in the nth function)
I can do it in some way or another, but I was wondering if there is a simple way to do it.
Or, without a library, you can just make use of map-indexed:
(def I #{1 3 4})
(def L '(1 2 3 4 5 6 7))
(defn g [n] (* n n))
(map-indexed #(if (I %) (g %2) %2) L))
; or, with explicit parameters
(map-indexed (fn [i n] (if (I i) (g n) n)) L)
; Both produce a lazy-list of (1 4 3 16 25 6 7)
Of course, with better names, this would be a lot more readable.
I have I as a set here instead of a list so lookups can be done efficiently. If you have it as a list originally, you can convert it to a set using set.
Also note, this produces a lazy-list. If you want a different structure, you can use vec for example to force it into a vector afterward.
(require '[com.rpl.specter :as s])
(use '[plumbing.core])
(s/transform [s/INDEXED-VALS (fn->> first (contains? #{1 3 4})) s/LAST]
(fn [x] (* x x))
'(1 2 3 4 5 6 7))
I would say, you can do it with a simple map call:
(defn g [x]
(* x x))
(def L '(1 2 3 4 5 6 7))
(def I '(1 3 4))
(map #(g (nth L %)) I)
;; => (4 16 25)
The mindset here is, for each indexes of I, I lookup the associated value in L and I compute g function over it.
Another option is to loop over the desired indexes to change, using assoc to replace items in a vector:
(ns tst.demo.core
(:use tupelo.core tupelo.test) )
(defn update-idx
[data txfn idxs]
(reduce
(fn [result idx]
(let [val-orig (get result idx)
val-new (txfn val-orig)]
(assoc result idx val-new)))
(vec data) ; coerce data to vector
idxs))
(dotest
(let [plus100 (fn [x] (+ 100 x))]
(is= (update-idx (range 10) plus100 [2 3 5 7]))
[0 1 102 103 4 105 6 107 8 9]))

How do I create a function that inserts an element between each pair of elements in a vector

I want to write a function that inserts elements between existing elements in a vector. The inserted elements are a function of the elements that precede and succeed it, with the first and last elements remaining unaffected.
E.g. I want inserted elements to be the mean of the elements that precede and succeed it:
Input:
[1 10 15]
Output:
[1 5.5 10 12.5 15]
What is the best way to do this in Clojure?
Here's another way:
(defn insert-mean-between [xs]
(let [f (fn [x y]
[(* (+ x y) 0.5) y])]
(->> xs
(partition 2 1)
(mapcat (partial apply f))
(cons (first xs))
vec)))
(insert-mean-between [1 10 15])
;;=> [1 5.5 10 12.5 15]
The main trick is that f is returning the answer and the RHS input. This way later on they will all compose together without repeats. The only problem you will have is that the first element is missing. So we just cons it onto the front. From the outset we had to know that cons would be a convenient operation when we chose to be returning the RHS rather than the LHS.
As calculating the mean was just an example, an improved solution would be for the inserting to be independent of the mean/whatever function:
(defn calc-mean [x y] (* (+ x y) 0.5)
(insert-between calc-mean [1 10 15])
Then a more general inserting function might be:
(defn insert-between [g xs]
(->> xs
(partition 2 1)
(mapcat (fn [[x y]] [(g x y) y]))
(cons (first xs))))
and the list of variants won't be complete without the recursive lazy sequence generation:
(defn with-avg [[x1 & [x2 :as tail] :as items]]
(when (seq items)
(if (seq tail)
(lazy-cat [x1 (/ (+ x1 x2) 2)] (with-avg tail))
[x1])))
user> (with-avg [1 2 3 4 5])
;;=> (1 3/2 2 5/2 3 7/2 4 9/2 5)
user> (with-avg [1])
;;=> [1]
user> (with-avg [])
;;=> nil
user> (with-avg [1 2])
;;=> (1 3/2 2)
user> (with-avg [1 2 3])
;;=>(1 3/2 2 5/2 3)
One way I could solve it is pattern matching Vector as f s t, I'm assuming it has 3 elements
Then create variable to assign first median first + second / 2 and second median second + third /2.
At the end return a new Vector with a combination you want.
Example, (I'm using lein REPL)
user=> (defn insert_medians[vect]
#_=> (let [[f s t] vect
#_=> m1 (float (/ (+ f s) 2))
#_=> m2 (float (/ (+ s t) 2))]
#_=> [f m1 s m2 t]))
#'user/insert_medians
user=> (insert_medians [1 10 15])
[1 5.5 10 12.5 15]
If a vector is larger than 3 elems, you need to find all the medians first and then insert into the original vector using interleave fn.
(defn insert-between
"Inserts elements between existing elements in a vector v. The inserted
elements are a result of applying the function f to the elements that precede
and succeed it, with the first and last elements of v remaining unaffected."
[f [x & xs :as v]]
(->> (partition 2 1 v)
(mapcat (fn [[a b]] [(f a b) b]))
(cons x)
(into [])))
(defn mean [& numbers]
(float (/ (apply + numbers) (count numbers))))
(insert-between mean [1 10 15]) ; => [1 5.5 10 10 12.5 15]
(insert-between + [1 10 15 20 25]) ; => [1 11 10 25 15 35 20 45 25]
(insert-between mean []) ; => [nil] :(

Is this poor 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

Whats the difference between (concat [x] y) and (cons x y)?

I am stuck at the Pascal's Trapezoid from 4Clojure site, where you need to build a lazy sequence of the trapezoid's numbers.
My first shot was this:
(defn pascal [x]
(cons x
(lazy-seq
(pascal
(map +
(cons 0 x)
(conj x 0)
)))))
Which didn't work:
user=> (take 5 (pascal [1 1]))
([1 1] (1 2 1) (0 2 4 2) (0 0 4 8 4) (0 0 0 8 16 8))
Writing it this way works, however:
(defn pascal2 [x]
(cons x
(lazy-seq
(pascal2
(map +
(concat [0] x)
(concat x [0])
)))))
user=> (take 5 (pascal2 [1 1]))
([1 1] (1 2 1) (1 3 3 1) (1 4 6 4 1) (1 5 10 10 5 1))
So, what exactly am I doing wrong here? What is the difference between cons/conj and concat?
As others stated conj inserts the element(s) it receives in a different position depending on the concrete collection type, see this SO question for more detailed information about the difference between conj and cons.
In the first version of your pascal function you are providing a vector as the initial argument so the expression (conj x 0) will insert the 0 at the end of the vector for the computation of the second element in the series, but since map returns a lazy sequence, when the third element is computed the insertion happens at the beginning ((conj (map inc '(0)) 2) ;= (2 1)), which results in wrong elements in the series from then on.
In order to use the cons and conj approach you have to make sure you return a vector by using mapv instead of map.
(defn pascal [x]
(cons x
(lazy-seq
(pascal
(mapv +
(cons 0 x)
(conj x 0))))))
(take 5 (pascal [1 1]))
;= ([1 1] [1 2 1] [1 3 3 1] [1 4 6 4 1] [1 5 10 10 5 1])
The drawback with mapv is that it is eager so it will compute all members in the pascal element, instead of just holding it back until you actually need them.
On the other hand, when using concat you do ensure you append the element at the end and that everything is lazy, but the append is not done cheaply like with vectors, see here for more information.
Regardless of these factors you can still use cons in both cases, since what it does is what you need in either case (i.e. have an element inserted at the beginning of a collection).
(defn pascal2 [x]
(cons x
(lazy-seq
(pascal2
(map +
(cons 0 x) ; Replaced concat with cons
(concat x [0]))))))
(take 5 (pascal2 [1 1]))
;= ([1 1] (1 2 1) (1 3 3 1) (1 4 6 4 1) (1 5 10 10 5 1))
According to ClojureDocs
conj
conj clojure.core
(conj coll x)
(conj coll x & xs)
conj[oin]. Returns a new collection with the xs 'added'. (conj nil
item) returns (item). The 'addition' may happen at different 'places'
depending on the concrete type.
conj accepts the first argument as a collection, which means coll must be a collection type. conj will return a new collection with x added into coll, and the place of x added is depending on the type of coll.
e.g.
> (conj [1] [0])
[1 [0]] ; See [0] is added into [1] as an element. Instead of returning [1 0], it returns [1 [0]]
> (conj [1] 0)
[1 0]
> (conj '(1) 0)
(0 1) ;See the element `0` position is different in each case.
concat
concat clojure.core
(concat)
(concat x)
(concat x y)
(concat x y & zs)
Returns a lazy seq representing the concatenation of the elements in
the supplied colls.
concat accepts all the argument as collection types, which is different from conj. concat returns the concatenation of arguments.
e.g.
> (concat [0] [1])
(0 1)
> (concat [0] [[1]])
(0 [1])
> (concat [0] 1) ;See the second argument is not a collection type, thus the function throws an exception.
java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Long
>
cons
cons clojure.core
(cons x seq)
Returns a new seq where x is the first element and seq is the rest.
The doc of cons states clearly how cons would work. The second argument of cons must be a seq.
e.g.
> (cons [1] [0])
([1] 0) ; [1] is the first element and (0) is the rest
> (first (cons [1] [0]))
[1]
> (rest (cons [1] [0]))
(0)
> (cons 1 [0]) ; 1 is the first element and (0) is the rest
(1 0)
> (cons [1] 0) ;the second argument is not a seq, throwing exception
java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Long
conj on a list adds the element to the front of the list.
If you convert the list to a vector it will work.
user=> (conj '(1 2 3) 4)
(4 1 2 3)
user=> (conj [1 2 3] 4)
[1 2 3 4]

In clojure, how to build lazy sequence using iterate function

The clojure document gives the following examples:
(take 10 (iterate (partial + 2) 0))
(def powers-of-two (iterate (partial * 2) 1))
(take 10 powers-of-two)
(def fib (map first (iterate (fn [[a b]] [b (+ a b)]) [1 1])))
(take 10 fib)
Anyone can explain the syntax of clojure's iterate function in more detail? I am very confused with all the usage. Why two brackets are there in (fn [[a b]] [b (+ a b)])?
Another example can be found here:
(defn iter [[x y]]
(vector y (+ x y)))
(nth (iterate iter [0 1]) 10000)
iterate takes a function f and an initial value x and produces a lazy sequence. The first element in the seq is x. Each subsequent element is computed by calling f with the previous element.
Example 1:
(iterate (partial + 2) 0)
This generates a sequence, starting at 0, where each element is the previous element with 2 added to it. I.e.:
0
(+ 2 0) ; => 2
(+ 2 2) ; => 4
(+ 2 4) ; => 6
; etc
Each element in the seq is passed to (partial + 2) when generating the following element.
Example 2:
(iterate (partial * 2) 1)
This generates a sequence, starting at 1, where each element is the previous element multiplied by 2. I.e.:
1
(* 2 1) ; => 2
(* 2 2) ; => 4
(* 2 4) ; => 8
(* 2 8) ; => 16
; etc
Again, you can see how each element feeds into the generation of the next one.
Example 3:
(iterate (fn [[a b]] [b (+ a b)]) [1 1])
Firstly, (fn [[a b]] ...) is a way to destructure a value into parts. In this case, the function accepts a two-element vector and unpacks it into the local variables a and b.
The function returns a two-element vector containing b and the sum of a and b (i.e. the second value in the previous pair and the sum of both values in the previous pair).
With this in mind, this iterate call generates:
[1 1]
[1 (+ 1 1)] ; => [1 2]
[2 (+ 1 2)] ; => [2 3]
[3 (+ 2 3)] ; => [3 5]
[5 (+ 3 5)] ; => [5 8]
; etc
Then (map first ...) grabs the first value in each pair, which gives you your Fibonacci sequence.