How to explain the &env example in book <Clojure Programming> - clojure

There's an example in the book (P252), it's about the &env in macro, I just don't understand how to get the result{x 1, y 2}
(defmacro spy-env []
(let [ks (keys &env)]
`(prn (zipmap '~ks [~#ks]))))
(let [x 1 y 2]
(spy-env)
(+ x y))
; {x 1, y 2}
;= 3
I tried in the REPL like this:
user=> (defmacro spy-env [] (let [ks (keys &env)] `(prn ~#(keys &env))))
#'user/spy-env
user=> (let [x 1 y 2] (spy-env))
2 1
It's wried.

Expanding the macro would be a great way for you to see how it works, but unfortunately macroexpand-1 doesn't convey the right bindings for &env. This example:
(let [x 1 y 2] (spy-env))
Actually expands out to:
(let [x 1 y 2] (prn (zipmap '(x y) [x y])))
Do you get it? Within the first sequence, x and y are not evaluated, but within the second, they are. So it zips the symbols to whatever they evaluate to. Within that let block, (zipmap '(x y) [x y]) evaluates to {x 1 y 2}.

a) If you try the code of the book it does output the right value
user=> (defmacro spy-env []
#_=> (let [ks (keys &env)]
#_=> `(prn (zipmap '~ks [~#ks]))))
#'user/spy-env
user=> (let [x 1 y 2] (spy-env))
{x 1, y 2}
nil
user=> (let [x 1 y 2]
#_=> (spy-env)
#_=> (+ x y))
{x 1, y 2}
3
b) now your code is different
initial code :
`(prn (zipmap '~ks [~#ks]))
vs
`(prn ~#(keys &env))
with actually #(keys &env) being equal to #ks

Related

How to get an element in a matrix in clojure

I have a vector of vectors [[plate,'p1',0,1],[plate,'p2',0,2],[plate,'p3',1,1]] containing x,y positions of detected plates.
How do I retrieve the x position of plate p3?
It seems to be a simple task but I'm more familiar with python, so I'm not sure how to do this in clojure.
i would go with something like this:
(def data [[:plate "p1" 0 1] [:plate "p2" 0 2] [:plate "p3" 1 1]])
(some (fn [[_ v x y]] (when (= v "p3") [x y])) data)
;;=> [1 1]
(some (fn [[_ v x y]] (when (= v "p123") [x y])) data)
;;=> nil
(def p '[[plate,"p1",0,1],[plate,"p2",0,2],[plate,"p3",1,1]])
;; be aware, 'p1' you can use in Python, but because in Clojure `'` at beginning
;; of a symbol is parsed as `quote`, you can't use `''` instead of `""` in contrast to Python.
;; create a nested map out of the vec of vecs
(defn vecs-to-map [vecs]
(reduce (fn [m [_ id x y]] (assoc m id {:x x :y y}))
{}
vecs))
(def m (vecs-to-map p))
;;=> {"p1" {:x 0, :y 1}, "p2" {:x 0, :y 2}, "p3" {:x 1, :y 1}}
;; you can access such a nested list via `get-in` and giving the nested map
;; and the keys it should apply on it.
(get-in m ["p3" :x])
;;=> 1
Since the irregularity that one key is a string and the other a keyword is
not so nice, I would make out of them all keywords:
(defn vecs-to-map [vecs]
(reduce (fn [m [_ id x y]] (assoc m (keyword id) {:x x :y y}))
{}
vecs))
(def m (vecs-to-map p))
;; access it by:
(get-in m [:p3 :x])
;;=> 1
Additional Thoughts
We ignored the first element of the vec plate.
Let's say there exist also another vectors like
(def b '[[box "b1" 0 1] [box "b2" 0 2] [box "b3" 1 1]])
And if we want a nested map which contains :plate and :box in the
outer level as keys, we have to change the vecs-to-map function.
(defn vecs-to-map [vecs]
(reduce (fn [m [k id x y]] (assoc m (keyword k)
(assoc (get m (keyword k) {})
(keyword id) {:x x :y y})))
{}
vecs))
Then we can generate the map containing everything by:
(def m (vecs-to-map (concat p b)))
;; or alternatively in two steps:
;; (def m (vecs-to-map p))
;; (def m (merge m (vecs-to-map b)))
m
;; => {:plate {:p1 {:x 0, :y 1}, :p2 {:x 0, :y 2}, :p3 {:x 1, :y 1}}, :box {:b1 {:x 0, :y 1}, :b2 {:x 0, :y 2}, :b3 {:x 1, :y 1}}}
And we access the content by:
;; access through:
(get-in m [:plate :p3 :x])
;; => 1
(get-in m [:box :b2 :y])
;; => 2
You don't really provide much context on what you're trying to do but it feels like you want to filter the vector of tuples to those that have the symbol p3' in the second position and then return just the third and fourth elements of such a match?
If so, the following would work:
dev=> (def plate :plate)
#'dev/plate
dev=> (def data [[plate,'p1',0,1],[plate,'p2',0,2],[plate,'p3',1,1]])
#'dev/data
dev=> (let [[[_ _ x y]] (filter (comp #{'p3'} second) data)]
#_=> [x y])
[1 1]
This doesn't feel very idiomatic, so perhaps you could explain more of the context?
Note: 'p3' is a symbol whose name is p3' so I wonder if you mean "p3" for a string?
The vector of vector format doesn't seem very conducive to the sort of access you want to perform - perhaps changing it to a hash map, whose keys are the plate IDs (if that's what p1, p2, and p3 are?) would be better to work with?
Edit: in response to #leetwinkski's note about the result when there is no match, here's an alternative:
You could use when-first:
dev=> (when-first [[_ _ x y] (filter (comp #{'p123'} second) data)]
#_=> [x y])
nil
dev=> (when-first [[_ _ x y] (filter (comp #{'p3'} second) data)]
#_=> [x y])
[1 1]
Here is how I would do it, based on my favorite template project. Please also note that in Clojure strings always use double-quotes like "p1". Single quotes are totally different!
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(defn vec-has-label
"Returns true iff a vector has the indicated label"
[vec lbl]
(= lbl (nth vec 1)))
(defn vec->x
"Given a vector, return the x value"
[vec]
(nth vec 2))
(defn find-label
[matrix label]
(let [tgt-vecs (filterv #(vec-has-label % label) matrix) ; find all matching vectors
x-vals (mapv vec->x tgt-vecs)] ; extract the x values
x-vals))
The unit tests show the code in action
(dotest
(isnt (vec-has-label '[plate "p1" 0 1] "p0"))
(is (vec-has-label '[plate "p1" 0 1] "p1"))
(is= 9 (vec->x '[plate "p1" 9 1]))
(let [matrix (quote
[[plate "p1" 0 1]
[plate "p2" 0 2]
[plate "p3" 1 1]])]
(is= (find-label matrix "p3")
[1])
))
The unit test show the 2 ways of "quoting" a data structure that contains one or more symbols. This would be unneeded if the redundant plate symbol weren't present.
See also this list of documentation sources.

quick way to apply hash-map to anonymous function in clojure

My question is whether given a hashmap
(def my-map {'x 1 'y 2 'z})
I can apply it to an anonymous function,
(fn [x y z] (+ x (* y z))
so that the arguments match the keys in the map, somthing like
(apply-ish my-map (fn [x y z] (+ x (* y z)))
Is there an easy fix to this problem? I feel like there is but I cant figure it out.
You can use map destructuring:
user> (def my-map {'x 1 'y 2 'z 3})
#'user/my-map
user> ((fn [{x 'x y 'y z 'z}] (+ x (* y z))) my-map)
7
You can simplify a bit with this form of desctructuring:
user> ((fn [{:syms [x y z]}] (+ x (* y z))) my-map)
7
or if you use keywords for your map keys:
user> (def my-map2 {:x 1 :y 2 :z 3})
#'user/my-map2
user> ((fn [{:keys [x y z]}] (+ x (* y z))) my-map2)
7
personally, i would not modify the function to accept the map as the arg, since it makes the function itself way less generic. The alternative (and idiomatic i guess, for any language) solution is to select needed keys from the map before passing them to function. That is quite easy, since both map and symbol (and keyword too) have function semantics in clojure:
user> (apply f (map my-map ['x 'y 'z]))
;;=> 7
user> (apply f ((juxt 'x 'y 'z) my-map))
;;=> 7

clojure: initialize 2d vector

in clojure I'm using the following function to initialize a 2d vector:
(defn vec2d [x y init]
(vec (map
#(vec (map init (range x))) (range y))))
usage:
(def grid (vec2d 40 30 (fn [] "x")))
Since I'm new to the language, I ask myself if this is the most straight-forward way to do so. Has anyone an idea to optimize this?
Under the assumption that you'll always want to initialize the entries of the vector to a constant, here's how I'd do this:
(defn vec2d
"Return an x by y vector with all entries equal to val."
[x y val]
(vec (repeat y (vec (repeat x val)))))
if you want to be able to initialize according to the coordinate, you could do this
(defn vec2d [sx sy f]
(mapv (fn[x](mapv (fn[y] (f x y)) (range sx))) (range sy)))
This allows you to have a constant value if you do
(vec2d 4 3 (constantly 2))
;; [[2 2 2 2] [2 2 2 2] [2 2 2 2]]
or initialize according to the coordinates, say:
(vec2d 4 3 (fn[x y] (+ x y)))
(vec2d 4 3 +) ;; or more concisely
;;[[0 1 2 3] [1 2 3 4] [2 3 4 5]]
Use mapv and you're set
(defn vec2d [x y init]
(mapv #(mapv init (range x)) (range y)))
If you want the coordinates for initialization:
(defn vec2d [x y init]
(mapv (fn [y] (mapv #(init % y) (range x))) (range y))
A nested for it's also very readable for boards generation if you just want all cells:
(defn vec2d [cols rows init]
(for [x (range rows) y (range cols)]
(init x y)))
Or if you don't mind seqs:
(defn vec2d [cols rows init]
(for [x (range rows)]
(for [y (range cols)]
(init x y))))
Using a threading macro:
(defn vec-2d [r c v] (->> (repeat c v) vec (repeat r) vec))

make-keyword-map in Clojure - Idiomatic?

I have been writing some Clojure recently, and I found myself using the following pattern frequently enough:
(let [x (bam)
y (boom)]
{:x x
:y y})
So I went ahead and wrote the following macro:
(defmacro make-keyword-map [& syms]
`(hash-map ~#(mapcat (fn [s] [(keyword (name s)) s]) syms)))
With that, code now looks like:
(let [x (bam)
y (boom)]
(make-keyword-map x y)
Would this sort of macro be considered idiomatic? Or am I doing something wrong, and missing some already established pattern to deal with something of this sort?
Note, that you can also replace all of:
(let [x (bam)
y (boom)]
{:x x
:y y})
with just:
{:x (bam)
:y (boom)}
which will evaluate to the same thing.
If your let expressions depend on one another, then how about a macro like so:
(defmacro make-keyword-map [& let-body]
(let [keywords-vals (flatten (map (juxt keyword identity)
(map first (partition 2 let-body))))]
`(let ~(vec let-body)
(hash-map ~#keywords-vals))))
that way (make-keyword-map x (foo 1) y (bar 2) z (zoom x)) expands to:
(clojure.core/let [x (foo 1) y (bar 2) z (zoom x)]
(clojure.core/hash-map :x x :y y :z z))
So something like this would work:
user=> (defn foo [x] (+ x 1))
#'user/foo
user=> (defn bar [x] (* x 2))
#'user/bar
user=> (defn zoom [x] [(* x 100) "zoom!"])
#'user/zoom
user=> (make-keyword-map x (foo 1) y (bar 2) z (zoom x))
{:z [200 "zoom!"], :y 4, :x 2}
Not sure how idiomatic that is, but it also saves you a let, compared to your original example.

How to append to variable from within another function in clojure?

The question doesn't really explain what I want to do but I couldn't think of anything else.
I have an empty map in the outer let function in a piece of code, and an integer array.
I want to iterate through the integer array, perform a simple task, and keep appending the resulting map to the variables in the outer variables.
(let [a {} ;outer variables
b {}]
(doseq [x [1 2 3]]
(let [r (merge a {x (* x x)}) ;I want to append this to a
s (merge b {x (+ x x)})] ;and this to b
(println (str "--a--" r "--b--" s)))))
But as soon as I get out of doseq, my a and b vars are still empty. I get that the scope of a and b doesn't extend outside of doseq for it to persist any changes done from within and that they are immutable.
How do I calculate the values of a and b in such cases, please? I tried to extract the functionality of doseq into another function and calling let with:
(let [a (do-that-function)])
etc but even then I couldn't figure out a way to keep track of all the modifications within doseq loop to then send back as a whole.
Am I approaching this in a wrong way?
Thanks
edit
Really, what I'm trying to do is this:
(let [a (doseq [x [1 2 3]] {x (* x x)})]
(println a))
but doseq returns nil so a is going to be nil :-s
All variables in clojure are immutable. If you need a mutable state you should use atoms or refs.
But in your case you can simply switch from doseq to for:
(let [a (for [x [1 2 3]] {x (* x x)})]
(println a))
Here is an example of solving your problem with atoms:
(let [a (atom {})
b (atom {})]
(doseq [x [1 2 3]]
(swap! a assoc x (* x x))
(swap! b assoc x (+ x x)))
(println "a:" #a)
(println "b:" #b))
But you should avoid using mutable state as far as possible:
(let [l [1 2 3]
a (zipmap l (map * l l))
b (zipmap l (map + l l))]
(println "a:" a)
(println "b:" b))
The trick is to think in terms of flows of data adding to existing data making new data, instead of changing past data. For your specific problem, where a data structure is being built, reduce is typically used:
(reduce (fn [result x] (assoc result x (* x x))) {} [1 2 3])
hehe, I just noticed that "reduce" might seem confusing given that it's building something, but the meaning is that a collection of things is "reduced" to one thing. In this case, we give reduce an empty map to begin with, which binds to result in the fn, and each successive mapping over the collection results in a new result, which we add to again with assoc.
You could also say:
(into {} (map (fn [x] [x (* x x)]) [1 2 3]))
In your question you wanted to make multiple things at once from a single collection. Here's one way to do that:
(reduce (fn [[a b] x] [(assoc a x (* x x)) (assoc b x (+ x x))]) [{} {}] [1 2 3])
Here we used destructuring syntax to refer to our two result structures - just make a picture of the data [with [vectors]]. Note that reduce is still only returning one thing - a vector in this case.
And, we could generalize that:
(defn xfn [n fs]
(reduce
(fn [results x] (map (fn [r f] (assoc r x (f x x))) results fs))
(repeat (count fs) {}) (range n)))
=> (xfn 4 [* + -])
({3 9, 2 4, 1 1, 0 0} {3 6, 2 4, 1 2, 0 0} {3 0, 2 0, 1 0, 0 0})
The result is a list of maps. And if you wanted to take intermediate steps in the building of these results, you could change reduce to reductions. Generally, map for transforming collections, reduce for building a single result from a collection.