Clojure binding of dynamic var not working as expected - clojure

From what I understand, setting a new binding on a dynamic var affects all functions called within that binding, and all functions called from those functions.
Why does the binding appear to be lost in the first example below?
(def ^:dynamic *out-dir* "/home/user")
(binding [*out-dir* "/home/dave"] (map #(str *out-dir* %) [1 2 3]))
; gives: ("/home/user1" "/home/user2" "/home/user3")
; expected: ("/home/dave1" "/home/dave2" "/home/dave3")
(binding [*out-dir* "/home/dave"] (conj (map #(str *out-dir* %) [1 2 3]) *out-dir*))
; gives: ("/home/dave" "/home/dave1" "/home/dave2" "/home/dave3")

This is caused by lazyness - map returns a lazy sequence which is defined inside the binding but is evaluated outside. You need to force the evaluation from inside:
(binding [*out-dir* "/home/dave"]
(doall (map #(str *out-dir* %) [1 2 3])))

It's true that laziness and dynamic bindings can cause problems; however, abandoning laziness is not the only solution. If you wish to preserve laziness (or to use dynamic bindings with pmap), use bound-fn or bound-fn*.
(def ^:dynamic x 0)
=> (binding [x 3] (map #(+ x %) (range 10)))
;; (0 1 2 3 4 5 6 7 8 9)
=> (binding [x 3] (map (bound-fn [y] (+ x y)) (range 10)))
;; (3 4 5 6 7 8 9 10 11 12)
=> (binding [x 3] (map (bound-fn* #(+ % x)) (range 10)))
;; (3 4 5 6 7 8 9 10 11 12)

Another solution is to use Python-style generator functions available via lazy-gen and yield from the Tupelo library:
(ns tst.demo.core
(:use demo.core tupelo.test)
(:require
[tupelo.core :as t] ))
(t/refer-tupelo)
(def ^:dynamic foo 1)
(dotest
(let [result (binding [foo 3]
(lazy-gen
(doseq [x (range 3)]
(yield {:foo foo :x x})))) ]
(println result)))
result => ({:foo 3, :x 0}
{:foo 3, :x 1}
{:foo 3, :x 2})

Related

Unquoting without namespace

I need to quote without namespace and combine it with unquoting. Something like:
'[a b ~c]
Unfortunately, unquoting works only with syntactic quoting:
`[a b ~c]
But then it expands to
[user/a user/b 7]
I would like to expand without namespaces.
What was suggested on clojurians slack channel is as follows:
Use a combination of "quote unquote" for symbols to get rid of namespaces:
`[~'a ~'b ~c]
and this works perfectly.
As a reference, I have been working on a similar capability that doesn't require defensive treatment like ~'a for each symbol you wish to remain unchanged. It isn't published yet, but here is the technique:
;-----------------------------------------------------------------------------
(defn unquote-form?
[arg]
(and (list? arg)
(= (quote unquote) (first arg))))
(defn unquote-splicing-form?
[arg]
(and (list? arg)
(= (quote unquote-splicing) (first arg))))
(defn quote-template-impl
[form]
(walk/prewalk
(fn [item]
(cond
(unquote-form? item) (eval (xsecond item))
(sequential? item) (let [unquoted-vec (apply glue
(forv [it item]
(if (unquote-splicing-form? it)
(eval (xsecond it))
[it])))
final-result (if (list? item)
(t/->list unquoted-vec)
unquoted-vec)]
final-result)
:else item))
form))
(defmacro quote-template
[form]
(quote-template-impl form))
and unit tests to show it in action:
;-----------------------------------------------------------------------------
(def vec234 [2 3 4])
(dotest
(is (td/unquote-form? (quote (unquote (+ 2 3)))))
(is (td/unquote-splicing-form? (quote (unquote-splicing (+ 2 3)))))
(is= (td/quote-template {:a 1 :b (unquote (+ 2 3))})
{:a 1, :b 5})
(is= (td/quote-template {:a 1 :b (unquote (vec (range 3)))})
{:a 1, :b [0 1 2]})
(is= (td/quote-template {:a 1 :b (unquote vec234)})
{:a 1, :b [2 3 4]})
(let [result (td/quote-template (list 1 2 (unquote (inc 2)) 4 5))]
(is (list? result))
(is= result (quote (1 2 3 4 5))))
(is= (td/quote-template [1 (unquote-splicing vec234) 5]) ; unqualified name OK here
[1 2 3 4 5])
(is= (td/quote-template [1 (unquote-splicing (t/thru 2 4)) 5])
[1 2 3 4 5])
(is= (td/quote-template [1 (unquote (t/thru 2 4)) 5])
[1 [2 3 4] 5])
)
So, instead of Clojure's syntax-quote (i.e. backquote), you can use quote-template. Then, use either unquote or unquote-splicing to insert values into the quoted template without having the namespace prepended to other symbols.
I know it is probably not the answer you are looking for by why not use code to construct that thing you want? Why force it to be all done in one expression? You could for example use
(conj '[a b] c)

Why doesn't map convey dynamic bindings? [duplicate]

From what I understand, setting a new binding on a dynamic var affects all functions called within that binding, and all functions called from those functions.
Why does the binding appear to be lost in the first example below?
(def ^:dynamic *out-dir* "/home/user")
(binding [*out-dir* "/home/dave"] (map #(str *out-dir* %) [1 2 3]))
; gives: ("/home/user1" "/home/user2" "/home/user3")
; expected: ("/home/dave1" "/home/dave2" "/home/dave3")
(binding [*out-dir* "/home/dave"] (conj (map #(str *out-dir* %) [1 2 3]) *out-dir*))
; gives: ("/home/dave" "/home/dave1" "/home/dave2" "/home/dave3")
This is caused by lazyness - map returns a lazy sequence which is defined inside the binding but is evaluated outside. You need to force the evaluation from inside:
(binding [*out-dir* "/home/dave"]
(doall (map #(str *out-dir* %) [1 2 3])))
It's true that laziness and dynamic bindings can cause problems; however, abandoning laziness is not the only solution. If you wish to preserve laziness (or to use dynamic bindings with pmap), use bound-fn or bound-fn*.
(def ^:dynamic x 0)
=> (binding [x 3] (map #(+ x %) (range 10)))
;; (0 1 2 3 4 5 6 7 8 9)
=> (binding [x 3] (map (bound-fn [y] (+ x y)) (range 10)))
;; (3 4 5 6 7 8 9 10 11 12)
=> (binding [x 3] (map (bound-fn* #(+ % x)) (range 10)))
;; (3 4 5 6 7 8 9 10 11 12)
Another solution is to use Python-style generator functions available via lazy-gen and yield from the Tupelo library:
(ns tst.demo.core
(:use demo.core tupelo.test)
(:require
[tupelo.core :as t] ))
(t/refer-tupelo)
(def ^:dynamic foo 1)
(dotest
(let [result (binding [foo 3]
(lazy-gen
(doseq [x (range 3)]
(yield {:foo foo :x x})))) ]
(println result)))
result => ({:foo 3, :x 0}
{:foo 3, :x 1}
{:foo 3, :x 2})

Clojure - Function that returns all the indices of a vector of vectors

If I have a vector [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15]]]
How can I return the positions of each element in the vector?
For example 1 has index [0 0 0], 2 has index [0 0 1], etc
I want something like
(some-fn [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15]]] 1)
=> [0 0 0]
I know that if I have a vector [1 2 3 4], I can do (.indexOf [1 2 3 4] 1) => 0 but how can I extend this to vectors within vectors.
Thanks
and one more solution with zippers:
(require '[clojure.zip :as z])
(defn find-in-vec [x data]
(loop [curr (z/vector-zip data)]
(cond (z/end? curr) nil
(= x (z/node curr)) (let [path (rseq (conj (z/path curr) x))]
(reverse (map #(.indexOf %2 %1) path (rest path))))
:else (recur (z/next curr)))))
user> (find-in-vec 11 data)
(1 0 1)
user> (find-in-vec 12 data)
(1 1 0)
user> (find-in-vec 18 data)
nil
user> (find-in-vec 8 data)
(0 2 1)
the idea is to make a depth-first search for an item, and then reconstruct a path to it, indexing it.
Maybe something like this.
Unlike Asthor's answer it works for any nesting depth (until it runs out of stack). Their answer will give the indices of all items that match, while mine will return the first one. Which one you want depends on the specific use-case.
(defn indexed [coll]
(map-indexed vector coll))
(defn nested-index-of [coll target]
(letfn [(step [indices coll]
(reduce (fn [_ [i x]]
(if (sequential? x)
(when-let [result (step (conj indices i) x)]
(reduced result))
(when (= x target)
(reduced (conj indices i)))))
nil, (indexed coll)))]
(step [] coll)))
(def x [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15]]])
(nested-index-of x 2) ;=> [0 0 1]
(nested-index-of x 15) ;=> [2 1 0]
Edit: Target never changes, so the inner step fn doesn't need it as an argument.
Edit 2: Cause I'm procrastinating here, and recursion is a nice puzzle, maybe you wanted the indices of all matches.
You can tweak my first function slightly to carry around an accumulator.
(defn nested-indices-of [coll target]
(letfn [(step [indices acc coll]
(reduce (fn [acc [i x]]
(if (sequential? x)
(step (conj indices i) acc x)
(if (= x target)
(conj acc (conj indices i))
acc)))
acc, (indexed coll)))]
(step [] [] coll)))
(def y [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15 [16 17 4]]]])
(nested-indices-of y 4) ;=> [[0 1 0] [2 1 1 2]]
Vectors within vectors are no different to ints within vectors:
(.indexOf [[[1 2 3] [4 5 6] [7 8 9]] [[10 11] [12 13]] [[14] [15]]] [[14] [15]])
;;=> 2
The above might be a bit difficult to read, but [[14] [15]] is the third element.
Something like
(defn indexer [vec number]
(for [[x set1] (map-indexed vector vec)
[y set2] (map-indexed vector set1)
[z val] (map-indexed vector set2)
:when (= number val)]
[x y z]))
Written directly into here so not tested. Giving more context on what this would be used for might make it easier to give a good answer as this feels like something you shouldn't end up doing in Clojure.
You can also try and flatten the vectors in some way
An other solution to find the path of every occurrences of a given number.
Usually with functional programming you can go for broader, general, elegant, bite size solution. You will always be able to optimize using language constructs or techniques as you need (tail recursion, use of accumulator, use of lazy-seq, etc)
(defn indexes-of-value [v coll]
(into []
(comp (map-indexed #(if (== v %2) %1))
(remove nil?))
coll))
(defn coord' [v path coll]
(cond
;; node is a leaf: empty or coll of numbers
(or (empty? coll)
(number? (first coll)))
(when-let [indexes (seq (indexes-of-value v coll))]
(map #(conj path %) indexes))
;; node is branch: a coll of colls
(coll? (first coll))
(seq (sequence (comp (map-indexed vector)
(mapcat #(coord' v (conj path (first %)) (second %))))
coll))))
(defn coords [v coll] (coord' v [] coll))
Execution examples:
(def coll [[2 1] [] [7 8 9] [[] [1 2 2 3 2]]])
(coords 2 coll)
=> ([0 0] [3 1 1] [3 1 2] [3 1 4])
As a bonus you can write a function to test if paths are all valid:
(defn valid-coords? [v coll coords]
(->> coords
(map #(get-in coll %))
(remove #(== v %))
empty?))
and try the solution with input generated with clojure.spec:
(s/def ::leaf-vec (s/coll-of nat-int? :kind vector?))
(s/def ::branch-vec (s/or :branch (s/coll-of ::branch-vec :kind vector?
:min-count 1)
:leaf ::leaf-vec))
(let [v 1
coll (first (gen/sample (s/gen ::branch-vec) 1))
res (coords v coll)]
(println "generated coll: " coll)
(if-not (valid-coords? v coll res)
(println "Error:" res)
:ok))
Here is a function that can recursively search for a target value, keeping track of the indexes as it goes:
(ns tst.clj.core
(:use clj.core tupelo.test)
(:require [tupelo.core :as t] ))
(t/refer-tupelo)
(defn index-impl
[idxs data tgt]
(apply glue
(for [[idx val] (zip (range (count data)) data)]
(let [idxs-curr (append idxs idx)]
(if (sequential? val)
(index-impl idxs-curr val tgt)
(if (= val tgt)
[{:idxs idxs-curr :val val}]
[nil]))))))
(defn index [data tgt]
(keep-if not-nil? (index-impl [] data tgt)))
(dotest
(let [data-1 [1 2 3]
data-2 [[1 2 3]
[10 11]
[]]
data-3 [[[1 2 3]
[4 5 6]
[7 8 9]]
[[10 11]
[12 13]]
[[20]
[21]]
[[30]]
[[]]]
]
(spyx (index data-1 2))
(spyx (index data-2 10))
(spyx (index data-3 13))
(spyx (index data-3 21))
(spyx (index data-3 99))
))
with results:
(index data-1 2) => [{:idxs [1], :val 2}]
(index data-2 10) => [{:idxs [1 0], :val 10}]
(index data-3 13) => [{:idxs [1 1 1], :val 13}]
(index data-3 21) => [{:idxs [2 1 0], :val 21}]
(index data-3 99) => []
If we add repeated values we get the following:
data-4 [[[1 2 3]
[4 5 6]
[7 8 9]]
[[10 11]
[12 2]]
[[20]
[21]]
[[30]]
[[2]]]
(index data-4 2) => [{:idxs [0 0 1], :val 2}
{:idxs [1 1 1], :val 2}
{:idxs [4 0 0], :val 2}]

why doesn't concat take effect here?

Here is the code:
(def city-nodes (atom nil))
(def city-edges (atom nil))
(def player-pos (ref nil))
(def visited-nodes (ref #{}))
(def player-status (atom :in-progress))
(def ^:dynamic *node-num* 8)
(def ^:dynamic *edge-num* 5)
(def ^:dynamic *worm-num* 3)
(def ^:dynamic *cop-odds* 4)
(defn filterb
[pred coll]
(let [rt (filter pred coll)]
(if (empty? rt)
nil
rt)))
(defn all-nodes []
(range 1 (inc *node-num*)))
(all-nodes)
(defn rand-node []
(inc (rand-int *node-num*)))
(defn edge-pair [x y]
(when-not (= x y)
[[x y] [y x]]))
(defn sample-2diff-nodes []
(let [[x y] [(rand-node) (rand-node)]]
(if (= x y)
(sample-2diff-nodes)
[x y])))
(edge-pair 1 1)
(take 4 (repeatedly #(sample-2diff-nodes)))
(def ^:dynamic t-edges (take 4 (repeatedly #(sample-2diff-nodes))))
(defn make-edge-vec []
(->> (repeatedly #(sample-2diff-nodes))
(take *edge-num*)
(apply concat)
set
vec))
(binding [t-edges (take 4 (repeatedly #(sample-2diff-nodes)))])
(prn t-edges)
(binding [t-edges (make-edge-vec)])
(prn t-edges)
(binding [t-edges (apply concat t-edges)])
(prn t-edges)
Results:
(binding [t-edges (take 4 (repeatedly #(sample-2diff-nodes)))])
(prn t-edges)
(binding [t-edges (make-edge-vec)])
(prn t-edges)
(binding [t-edges (apply concat t-edges)])
(prn t-edges)
([6 1] [1 2] [5 2] [3 1])
([6 1] [1 2] [5 2] [3 1])
([6 1] [1 2] [5 2] [3 1])
nil
I for the last prn command, I expected ( 6 1 1 2 5 2 3 1).
the closing ) of the binding call is in the wrong place:
(binding [t-edges (apply concat t-edges)])
(prn t-edges)
should be:
(binding [t-edges (apply concat t-edges)]
(prn t-edges))
Though in this case you almost certainly want to use a let expression instead

how can the clojure.algo.generic library be used?

I know that the library https://github.com/clojure/algo.generic provides ways of implementing generic arithmetic operators + * / - but there I couldn't find a simple example of how to create them and then how to use it as a library.
say if I wanted to implement vector addition, etc:
> (+ [1 2 3 4 5] 5)
;; => [6 7 8 9 10]
how would I go about:
defining the + operator with algo.generic
using the + operator previously defined within another project?
(ns your.custom.operators
(:import
clojure.lang.IPersistentVector)
(:require
[clojure.algo.generic.arithmetic :as generic]))
(defmethod generic/+
[IPersistentVector Long]
[v x]
(mapv + v (repeat x)))
(ns your.consumer.project
(:refer-clojure :exclude (+))
(:use
[clojure.algo.generic.arithmetic :only (+)])
(:require
your.custom.operators))
(defn add-five
[v]
(+ v 5))
edit 2,
user=> (defn + [coll i] (map #(clojure.core/+ % i) coll))
#'user/+
user=> (+ [1 2 3 4 5] 5)
(6 7 8 9 10)
edit, You can also do
(in-ns 'algo.generic)
(defn + [& args])
-- Edit --
You should use (require [lib :as namespacehere]) and call (namespacehere/+ ...). Below is the code for the problem presented.
user=> (map #(+ % 5) [1 2 3 4 5])
(6 7 8 9 10)
Also, check out (in-ns).