I understand everything besides the last line. More specifically the (:x y) part. Why is pointing to 10? Also just to be sure... is the first element in y supposed to be pointing to the whole thing (10 and 20) or just the 20? Thank you!
In clojure, symbols with : as a prefix are keywords. Official docs. Keywords when used as functions have almost the same behavior as get. So the intention of (:x y) is (get y :x).
If we assume that there's a (deftype Double [x y]) in your program that doesn't appear in this image, this means in your last form the second element of your Tripple type is the field :x or .x of the Double y, being just the value (Double. 10, 20). You can test this at a REPL if you want...
user> (defrecord MyDouble [x y])
user.MyDouble
user> (defrecord MyTripple [x y z])
user.MyTripple
user> (def x (MyDouble. 10 20))
#'user/x
user> (def y (MyDouble. x 40))
#'user/y
user> (def z (MyTripple. x (:x y) y))
#'user/z
user> z
#user.MyTripple{:x #user.MyDouble{:x 10, :y 20}, :y #user.MyDouble{:x 10, :y 20}, :z #user.MyDouble{:x #user.MyDouble{:x 10, :y 20}, :y 40}}
user> (:x y)
#user.MyDouble{:x 10, :y 20}
The diagram is thoroughly confusing.
x is used for two distinct things:
the symbol for a record of type Double and
the name of a field of type Double.
Assuming that x is the name of the first field of Double, given
(def y (MyDouble. x 40))
then
(def z (Triple. x (:x y) y))
... is equivalent to
(def z (Triple. x x y))
Hence the two arrows pointing to Double{:x 10, :y 20}, the value of the global x (assuming the other field is named y).
And it is seldom helpful to consider these references as pointers. Records, like almost all the basic data structures in Clojure (only deftypes spring to mind as an exception), are immutable.
It's Shared Structure!
(defrecord MyDouble [x y]) ;; (defrecord Double [x y]) produces an exception
(defrecord Triple [a b c])
(def x (MyDouble. 10 20))
This creates a new MyDouble record with the values 10 and 20. (:x x) => 10, (:y x) => 20.
(def y (MyDouble. x 40))
This creates a new MyDouble record. Its first value is the value that x has at that moment, which is #user.MyDouble{:x 10, :y 30}. and 40. That is, y has the value: #user.MyDouble{:x #user.MyDouble{:x 10, :y 20}, :y 40}.
(def z (Triple. x (:x y) y))
This creates a new Triple. object. Just as with the previous line, the first value is #user.MyDouble{:x 10, :y 30}. The second value is the value of the expression (:x y):
user=> (:x #user.MyDouble{:x #user.MyDouble{:x 10, :y 20}, :y 40})
#user.MyDouble{:x 10, :y 20}
So the second value is also #user.MyDouble{:x 10, :y 20}. The third value is the value of y, or #user.MyDouble{:x #user.MyDouble{:x 10, :y 20}, :y 40}.
So z, printed out, is:
#user.Triple{:a #user.Mydouble{:x 10, :y 20},
:b #user.Mydouble{:x 10, :y 20},
:c #user.Mydouble{:x #user.Mydouble{:x 10, :y 20},
:y 40}}
What the diagram is showing you is that there is a lot of shared structure here. The #user.Mydouble{:x 10, :y 20}, you see repeated three times in z is, in fact, the exact same object, not three copies. And so yes, in answer to your last question, the arrow is pointing to the whole thing, not just to the number 20.
Related
I've got two functions here that I though would return the same thing but they don't. I can't for the life of me figure out why. Any ideas?
user=> (defn foo [x] (when (> x 0) (conj (vec (foo (dec x))) x)))
#'user/foo
user=> (defn bar [x] (when (> x 0) (conj [(bar (dec x))] x)))
#'user/bar
user=> (foo 5)
[1 2 3 4 5]
user=> (bar 5)
[[[[[nil 1] 2] 3] 4] 5]
[xs] creates a one-element vector, containing the single item xs. In contrast, (vec xs) does not create a one-element vector containing the item xs. Instead, it creates an n-element vector, containing each item in xs separately. A function which does the former is vector: (vector x y z) is conceptually the same as [x y z]. Compare to (list x) and (seq x): the former creates a one-element sequence containing its input, while the latter converts its input to a sequence.
Of course, if you are going to conj onto the result of vector you might as well just build the 2-element vector directly: instead of (conj (vector x) y), write [x y].
I am trying to make a function in Clojure that would take a map, x and y values as parameters and then go through all the elements in a map and potentially replace all x values (if any) with y.
(defn Change-x-to-y-in-map [map x y]; code here)
For example, if I would have a map like {:a 1 :b 2 :c 1} and I would call the function with parameters [map 1 "it works!"], the function should return the following map: {:a "it works!" :b 2 :c "it works!}.
So it would replace all keys with value 1 to keys with value "it works!".
Thank you already beforehand for your help!
You can do this generically over any form with clojure.walk functions:
(defn replace-vals [m v r]
(walk/postwalk
(fn [e] (if (= e v) r e))
m))
(replace-vals {:a 1 :b 2 :c 1} 1 "Hey!")
=> {:a "Hey!", :b 2, :c "Hey!"}
(replace-vals [1 2 3 4 1] 1 "Hey!")
=> ["Hey!" 2 3 4 "Hey!"]
This will also work for nested forms.
(replace-vals {:a 1 :b {:c 1 :d "Bye!"}} 1 "Hey!")
=> {:a "Hey!", :b {:c "Hey!", :d "Bye!"}}
If you want to only replace map values you could refactor to this:
(defn replace-map-vals [m v r]
(walk/prewalk
(fn [e] (if (and (map-entry? e) (= (val e) v))
[(key e) r]
e))
m))
(replace-map-vals {1 "not replaced" :replaced 1} 1 "Hey!")
=> {1 "not replaced", :replaced "Hey!"})
Note this version uses prewalk due to an issue with postwalk and map entries.
Starting from Stefan's answer, we can modify the initial map instead of building a new one:
(defn update-v [m ov nv]
(reduce-kv (fn [acc k v] (if (= v ov) (assoc acc k nv) acc))
m m))
And we can use a transient to make the modifications to:
(defn update-v [m ov nv]
(persistent!
(reduce-kv (fn [acc k v] (if (= v ov) (assoc! acc k nv) acc))
(transient m) m)))
These changes should (hmmmmmm) speed things up a little.
(I hereby put this code under Apache 2.0 license if you can't take it under the SO default CC-BY SA)
A reducible-friendly version would use reduce-kv directly:
(defn update-v [m ov nv]
(reduce-kv (fn [acc k v]
(assoc acc k (if (= v ov) nv v)))
{} m))
(update-v {:x 1 :y 2 :z 1} 1 "It Works!")
=> {:x "It Works!", :y 2, :z "It Works!"}
(I hereby put this code under Apache 2.0 license if you can't take it under the SO default CC-BY SA)
One way is to iterate through the keys and values of the map with for and use into to build up the new map:
(defn val-replace [m x y]
(into {} (for [[k v] m]
[k (if (= v x) y v)])))
> (val-replace {:x 1 :y 2 :z 1} 1 "It Works!")
{:x "It Works!", :y 2, :z "It Works!"}
> (val-replace1 {:a "" :b 4 :c ""} "" nil)
{:a nil, :b 4, :c nil}
Specter solution to boot:
(defn replace-vals [m v r]
(setval [MAP-VALS (partial = v)] r m))
The function map-vals already exists in the Tupelo library. It applies a function tx-fn to every value in the map, creating a new output map. The unit tests show it in action:
(let [map-123 {:a 1 :b 2 :c 3}
tx-fn {1 101 2 202 3 303}]
(is= (t/map-vals map-123 inc) {:a 2, :b 3, :c 4})
(is= (t/map-vals map-123 tx-fn) {:a 101, :b 202, :c 303}))
In the first case, the function inc is applied to each value in the input map (IM).
The 2nd example uses a "transformation map" as tx-fn, where each [k v] pair indicates the desired transformation from old to new values, respectivly, in IM. Thus we see values in map-123 changed as:
1 -> 101
2 -> 202
3 -> 303
A sister function map-keys is also available:
(dotest
(let [map-123 {1 :a 2 :b 3 :c}
tx-fn {1 101 2 202 3 303}]
(is= (t/map-keys map-123 inc) { 2 :a 3 :b 4 :c})
(is= (t/map-keys map-123 tx-fn) {101 :a 202 :b 303 :c}))
Suppose i had this atom as the state of a game:
(defonce state (atom {:player
{:cells [{:x 123 :y 456 :radius: 1.7 :area 10}
{:x 456 :y 789 :radius: 1.7 :area 10}
{...}]}}))
And i wanted to read where one of the cells is, (the maps :x and :y values in the :cells vector) calculate with those values where they should be in the next frame and then when calculated update the new :x and :y positions in the respective map?
I have this so far:
(let [cells (get-in #state [:player :cells])]
(reduce
(fn
[seq cell]
(let [cell-x (get-in cell [:x])
cell-y (get-in cell [:y])]
; do my calculations
; ...
))
nil
cells))
So i can read the values and do the calculation but how would i update the x and y position with the new values? I could use:
(swap! state update-in [:player :cells ...] assoc :x new-x :y new-y)
But there i don't know the index ... in which vector to update it in.
I assume there is a way without using reduce that would give me the index?
Or am i approaching this totally un-idiomatic?
You can update a particular hash-map object without knowing the index in the vector:
(let [when-x 123
new-x -1
new-y -1]
(swap! state update-in [:player :cells]
(fn [v] (mapv (fn [{:keys [x y] :as m}]
(if (= x when-x)
(assoc m :x new-x :y new-y)
m))
v))))
;;=> {:player {:cells [{:x -1, :y -1, :radius 1.7, :area 10}
;; {:x 456, :y 789, :radius 1.7, :area 10}]}}
This code is useful where you have some criterion for which potentially many values need to be updated. Note that here we don't need to know the index to do the update.
Going on a slight excursion now, but if you needed to update only a particular already known index then one way would be to use the same technique but with map-indexed instead of mapv:
(let [when-idx 1
new-x -1
new-y -1]
(swap! state update-in [:player :cells]
(fn [v] (vec (map-indexed (fn [n {:keys [x y] :as m}]
(if (= n when-idx)
(assoc m :x new-x :y new-y)
m))
v)))))
However that would be pointless with your data since a vector is an associative collection and thus update-in will be able to select by index:
(let [when-idx 0
new-x -1
new-y -1]
(swap! state update-in [:player :cells when-idx] #(assoc % :x new-x :y new-y)))
Interestingly note that it would not however be pointless if instead of a vector you had a list, so '({:x 123 :y 456 :radius 1.7 :area 10}{:x 456 :y 789 :radius 1.7 :area 10}). With this non-associative collection you cannot use update-in.
Another reason this construction would not be pointless is if you are worried about performance: you can use laziness to short-circuit finding the answer:
(defn replace-in [v [idx new-val]]
(concat (subvec v 0 idx)
[new-val]
(subvec v (inc idx))))
(let [when-x 123
new-x -1
new-y -1]
(swap! state update-in [:player :cells]
(fn [v] (->> v
(keep-indexed (fn [idx {:keys [x] :as m}]
(when (= x when-x)
[idx (assoc m :x new-x :y new-y)])))
first
(replace-in v)))))
keep-indexed is similar to map-indexed, except that any nil values are not returned into the output sequence. Once the first value is realised the rest of the potential values are never generated, hence the short-circuit. Here idx is used by calls to subvec to chop up the original vector and include the new hash-map object.
Let's say there is a nested map like below: (partially nested only)
(def mymap {:a 10
:b {:ba 21, :bb 22 :bc 23}
:c 30
:d {:da 41, :db 42}})
How can I apply a function, say #(* % 2), and update every value in this map? That is without specifying any key. The result will look like this:
{:a 20,
:b {:ba 42, :bb 44, :bc 46},
:c 60,
:d {:da 82, :db 84}}
So far, I came up with this own function:
(defn map-kv [f coll] (reduce-kv (fn [m k v] (assoc m k (f v))) (empty coll) coll))
But I still need to specify a first-level key and can't apply to all first-level and second-level keys values.
You may wish to review the postwalk function: https://clojuredocs.org/clojure.walk/postwalk
(def data
{:a 10
:b {:ba 21, :bb 22 :bc 23}
:c 30
:d {:da 41, :db 42}} )
(defn tx-nums [x]
(if (number? x)
(* 2 x)
x))
(postwalk tx-nums data) =>
{:a 20,
:b {:ba 42, :bb 44, :bc 46},
:c 60,
:d {:da 82, :db 84}}
Porthos3 makes a good point. The above will transform map keys as well as map values. If you want only values to change, you could use the map-vals function from the Tupelo Clojure library (the Medley lib has a similar function).
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[tupelo.core :as t]
[clojure.walk :as walk]))
(dotest
(let [data-2 {1 2
3 4}
tx-vals-fn (fn [item]
(if (map? item)
(t/map-vals item #(* 2 %))
item))
result (walk/postwalk tx-vals-fn data-2)]
(is= (spyx result) {1 4, 3 8})))
with result:
-------------------------------
Clojure 1.10.1 Java 13
-------------------------------
Testing tst.demo.core
result => {1 4, 3 8}
Ran 2 tests containing 1 assertions.
0 failures, 0 errors.
In addition to postwalk, as Alan mentioned, it is trivial to recursively explore the map and update every key. Clojure provides a function called fmap that simply applies a function to every value in a map. To use:
In project.clj, declare this dependency:
[org.clojure/algo.generic "0.1.2"]
And in your code, then require:
(require '[clojure.algo.generic.functor :as f :only [fmap]])
Then define a function that will walk your map recursively:
(defn fmap*
[f m]
(f/fmap #(if (map? %)
(fmap* f %)
(f %))
m))
(fmap*
(partial * 2) ;; double every number
{:a 21 :b {:x 11 :y 22 :z {:p 100 :q 200}}})
=> {:a 42, :b {:x 22, :y 44, :z {:p 200, :q 400}}}
In case you don't want to have to include a non-core function, here's the code for fmap used on a map, from the clojure source (adapted for a defn):
(defn fmap [f m]
(into (empty m) (for [[k v] m] [k (f v)])))
I really like specter, see https://github.com/nathanmarz/specter
If you exactly want to change the top 2 levels, calling transform twice is the simplest
(->> mymap
(sp/transform [sp/MAP-VALS map? sp/MAP-VALS number?] #(* 2 %))
(sp/transform [sp/MAP-VALS number?] #(* 2 %)))
You can implement the walk part in specter too, if you really want to replace everything recursively. For example, I wanted to floatify all numbers in an arbitrary structure. First, I had to define the walker (which also handles vectors, seq, and sets). This is generic, so I can reuse it.
(defprotocolpath WalkValues)
(extend-protocolpath WalkValues
clojure.lang.IPersistentVector [ALL WalkValues]
clojure.lang.IPersistentMap [MAP-VALS WalkValues]
clojure.lang.IPersistentSet [ALL WalkValues]
clojure.lang.ISeq [ALL WalkValues]
Object STAY)
but once I done that, I can implement it is
(sp/transform [sp/WalkValues integer?] float mymap)
or in this example
(sp/transform [sp/WalkValues number?] #(* 2 %) mymap)
(require '[clojure.walk :as walk])
(defn fmap [f m]
(into (empty m) (for [[k v] m] [k (f v)])))
(defn map-leaves
[f form]
(walk/postwalk (fn [m]
(if (map? m)
(fmap #(if (map? %) % (f %)) m)
m))
form))
example:
(map-leaves
(partial * 2)
{:a 10
:b {:ba 21, :bb 22 :bc 23}
:c 30
:d {:da 41, :db 42}})
;; {:a 20, :b {:ba 42, :bb 44, :bc 46}, :c 60, :d {:da 82, :db 84}}
explanation:
postwalk calls walk in its implementation.
(defn postwalk
[f form]
(walk (partial postwalk f) f form))
walk checks the type of the form and it matches the form (a map) against coll? and then maps inner (which is postwalk with f) against the form which matches map-entry?.
We don't want to "postwalk with f" against the key so we check to see if it's a map and skip it (return m) if it's not a map. (This logic fails if you use a map as a key.)
postwalk passed our f into walk as outer. The lambda inside map-leaves skips calling outer (aka f) on the resulting maps (look at the coll? match) as it backs out of the recursion. The maps were already transformed by the map inner.
(defn walk
[inner outer form]
(cond
(list? form) (outer (apply list (map inner form)))
(map-entry? form)
(outer (MapEntry. (inner (key form)) (inner (val form)) nil))
(seq? form) (outer (doall (map inner form)))
(record? form) (outer (reduce (fn [r x] (conj r (inner x))) form form))
(coll? form) (outer (into (empty form) (map inner form)))
:else (outer form)))
In a clojure profiling library, there is a an atom storing some profiling information and it is modified by a number of functions. In the comments, they assert that the summary statistics (one of the functions) is called last for efficiency purposes, but I could not see how this was enforced in the actual code.
I then noticed that one manner in which this could enforced is the ordering in the construction of a map, so I recreated a simpler example to demonstrate.
(defn t []
(let [a (atom {})]
{:b (swap! a #(assoc % :data 'is-here))
:c (keys #a)}))
Here, in agreement with the comment, which one of :b or :c comes first in the code will determine the value of a. Of course there must be some ordering, but is this behaviour guaranteed? It seems like it should not be, since an unordered hash has no ordering, and this could have detrimental implications for parallelism.
I would consider the evaluation order of map literals an implementation detail and using non-constant expressions with side effects (e.g. setting an atom state) in map literals to be asking for trouble.
For well defined evaluation semantics, use a function call, where the arguments are guaranteed to be evaluated from left to right.
(let [i (atom 0)]
(hash-map :a (swap! i inc) :b (swap! i inc) :c (swap! i inc)))
;=> {:a 1, :c 3, :b 2}
Note that the keys are out of order since we used hash-map, but the values correspond to the keys in the order written.
Implementation details
The implementation details of map literals (in version 1.5) depends on several things:
The reader treats the unevaluated map literals as an ordered array-map for small maps (8 pairs or fewer) or an unordered hash-map for larger maps.
The compiler-evaluator parses the map expression provided by the reader as an unordered hash-map irrespective of size if the keys and values are constant expressions, but will evaluate it as an array-map. Otherwise, the compiler-evaluator evaluates in the key order provided by the reader, dependent on size.
An small example will make this a bit clearer (perhaps):
user=> {:a 1, :b 2, :c 3}
{:a 1, :c 3, :b 2}
user=> (type *1)
clojure.lang.PersistentArrayMap
user=> (def m1 {:a 1, :b 2, :c 3})
#'user/m1
user=> m1
{:a 1, :c 3, :b 2}
user=> (type m1)
clojure.lang.PersistentHashMap
user=> (eval m1)
{:a 1, :c 3, :b 2}
user=> (type *1)
clojure.lang.PersistentArrayMap
But...
user=> (def m2 {:a ((fn [] 1)), :b 2, :c 3})
#'user/m2
user=> m2
{:a 1, :b 2, :c 3}
user=> (type m2)
clojure.lang.PersistentArrayMap
I believe, as long as the {...} form remains equal to or less than 8 map entires (key/value pairs), the order will be maintained. A {...} will become a PersistentArrayMap with 8 or fewer entries, and a PersistentHashMap otherwise. The former will preserve the given order of its map entries, a guarantee not extended to the latter. Source.
As for parallelism, I think the following simple test shows that map entries will get evaluated in the order they are given, for maps created with 8 or fewer entries:
user=> (let [x (atom 0)]
{:a (do (Thread/sleep 1000) (swap! x inc))
:b (swap! x inc)})
{:a 1, :b 2}
But may evaluate in some other order for maps created with greater than 8 entries:
user=> (let [x (atom 0)]
{:a (do (Thread/sleep 1000) (swap! x inc))
:b (swap! x inc)
:c (swap! x inc)
:d (swap! x inc)
:e (swap! x inc)
:f (swap! x inc)
:g (swap! x inc)
:h (swap! x inc)
:i (swap! x inc)})
{:a 1, :c 2, :b 3, :f 4, :g 5, :d 6, :e 7, :i 8, :h 9}
Though, at least for hash-maps at 9 entries, it doesn't seem like any parallelism is going on during their instantation. That's interesting.