Date periods in clojure - vector

I have a data structure like this:
[{ :2007-08-05 [ { :meat-weight-gain 100} {:meat-weight-loss 80} {:meat-balance 20}]},
{ :2007-08-06 [ { :meat-weight-gain 10} {:meat-weight-loss 60} {:meat-balance -30}]},
{ :2007-08-07 [ { :meat-weight-gain 40} {:meat-weight-loss 80} {:meat-balance -70}]}
{ :2007-08-08 [ { :meat-weight-gain 100} {:meat-weight-loss 0} {:meat-balance 30}]}]
How can i iterate through it and return the data period of when the meat balance was negative? A sample data would be something like this:
[ {:end-period-balance -70, :period-start 2007-08-06, :period-end 2007-08-07 } ]
Other than that, can I improve my data structure or it is already ok? If yes, how? Thank you very much.

i would advice you to change your data shape to a list of tuples, each containing date and map of balance data. Just like this:
(def data [[:2007-08-05 { :meat-weight-gain 100 :meat-weight-loss 80 :meat-balance 20}],
[:2007-08-06 { :meat-weight-gain 10 :meat-weight-loss 60 :meat-balance -30}],
[:2007-08-07 { :meat-weight-gain 40 :meat-weight-loss 80 :meat-balance -70}]
[:2007-08-08 { :meat-weight-gain 100 :meat-weight-loss 0 :meat-balance 30}]
[:2007-08-09 { :meat-weight-gain 19 :meat-weight-loss -20 :meat-balance -10}]])
then it would be easy to classify the periods by weight gain/loss (using partition-by) and collect needed info:
user> (let [parts (partition-by #(-> % second :meat-balance neg?) data)]
(keep #(let [[p-start _] (first %)
[p-end {balance :meat-balance}] (last %)]
(when (neg? balance)
{:period-start p-start
:period-end p-end
:end-period-balance balance}))
parts))
;;=> ({:period-start :2007-08-06, :period-end :2007-08-07, :end-period-balance -70}
;; {:period-start :2007-08-09, :period-end :2007-08-09, :end-period-balance -10})
or a list of maps including date:
(def data [{:date :2007-08-05 :meat-weight-gain 100 :meat-weight-loss 80 :meat-balance 20},
{:date :2007-08-06 :meat-weight-gain 10 :meat-weight-loss 60 :meat-balance -30},
{:date :2007-08-07 :meat-weight-gain 40 :meat-weight-loss 80 :meat-balance -70}
{:date :2007-08-08 :meat-weight-gain 100 :meat-weight-loss 0 :meat-balance 30}
{:date :2007-08-09 :meat-weight-gain 100 :meat-weight-loss 0 :meat-balance -10}])
user> (let [parts (partition-by #(-> % :meat-balance neg?) data)]
(keep #(let [{p-start :date} (first %)
{p-end :date balance :meat-balance} (last %)]
(when (neg? balance)
{:period-start p-start
:period-end p-end
:end-period-balance balance}))
parts))
;;=> ({:period-start :2007-08-06, :period-end :2007-08-07, :end-period-balance -70}
;; {:period-start :2007-08-09, :period-end :2007-08-09, :end-period-balance -10})
UPDATE
if you really need your initial data format, then you can use the same approach, just redefining values retrieval parts:
user> (defn meat-balance [rec]
(some :meat-balance (-> rec first second)))
user> (let [parts (partition-by #(-> % meat-balance neg?) data)]
(keep #(let [p-start (-> % first ffirst)
p-end (-> % last ffirst)
balance (-> % first meat-balance)]
(when (neg? balance)
{:period-start p-start
:period-end p-end
:end-period-balance balance}))
parts))
;;=> ({:period-start :2007-08-06, :period-end :2007-08-07, :end-period-balance -30})

Change the format of the data:
Consolidate the vector of data for each date into a single map
.
Make the whole thing a map, keyed by the date keywords.
Lose the :meat-weight-balance data - it's redundant.
(The first two changes follow #leetwinski's advice)
We get ...
(def data
{:2007-08-05 {:meat-weight-gain 100, :meat-weight-loss 80},
:2007-08-06 {:meat-weight-gain 10, :meat-weight-loss 60},
:2007-08-07 {:meat-weight-gain 40, :meat-weight-loss 80},
:2007-08-08 {:meat-weight-gain 100, :meat-weight-loss 0}})
The entries happen to be in date order, because it's a small map. If we want to ensure date order, we'd better have a sorted map:
(def sorted-data (into (sorted-map) data))
This doesn't look any different, but will always present the data in key order, which is - thankfully - date order.
This seems a long way round to get the records into the original order in the vector, but the vector has the unused date-keyword order cutting across it: Don't Repeat Yourself.
Let's calculate the daily balances:
(def balances
(map-vals #(- (:meat-weight-gain %) (:meat-weight-loss %)) sorted-data))
balances
=> {:2007-08-05 20, :2007-08-06 -50, :2007-08-07 -40, :2007-08-08 100}
... where the map-vals function is an analogue of map and mapv that works on the values of a map:
(defn map-vals [f m]
(into (empty m) (map (fn [[k v]] [k (f v)])) m))
Notice that it returns the same species of map as it's given, in this case a sorted one.
We want to know over what periods there was a net weight loss. It isn't clear what this means. Let's look at the net weight gains from the start:
(reductions (fn [[_ av] [k v]] [k (+ av v)]) balances)
=> ([:2007-08-05 20] [:2007-08-06 -30] [:2007-08-07 -70] [:2007-08-08 30])
Or we could partition the sequence into gaining and losing sections:
(partition-by (fn [[_ v]] (neg? v)) balances)
=> (([:2007-08-05 20]) ([:2007-08-06 -50] [:2007-08-07 -40]) ([:2007-08-08 100]))
We need a variant of partition-by that keys its sub-sequences by the value of the discriminating function, as group-by does. Then you know what's a gaining range and what's a losing one. A cheap and cheerful version is ...
(defn group-partition-by [f coll]
(let [parts (partition-by f coll)]
(map #(-> % first f (list %)) parts)))
Then
(group-partition-by (fn [[_ v]] (neg? v)) balances)
=> ((false ([:2007-08-05 20]))
(true ([:2007-08-06 -50] [:2007-08-07 -40]))
(false ([:2007-08-08 100])))
You might want to reduce this data to a (sorted) map from date-range to total balance.
Conversion
How do we get from given to data? We can get to sorted-data directly as follows:
(def sorted-data
(->> given
(into (sorted-map))
(map-vals (comp #(into {} %) #(remove :meat-balance %)))))
sorted-data
=>
{:2007-08-05 {:meat-weight-gain 100, :meat-weight-loss 80},
:2007-08-06 {:meat-weight-gain 10, :meat-weight-loss 60},
:2007-08-07 {:meat-weight-gain 40, :meat-weight-loss 80},
:2007-08-08 {:meat-weight-gain 100, :meat-weight-loss 0}}
Impressions
You have to get to know the sequence library thoroughly.
Corresponding facilities for maps aren't on the surface. Getting to
grips with transducers would help - not sure how much.
Note
You had better be using European, not American dates, otherwise you are going to need a cleverer keyfn to get the records in date sequence. I'd prefer clj-time local-dates to keywords as keys
in case the code crosses the Atlantic;
so that you can run validity checks, such as that you have a record
for every day.

As it already has been said above, your data are not structured well for such a purpose. Here is a step-by-step solution:
Prepare your data:
(def data
[{ :2007-08-05 [ { :meat-weight-gain 100} {:meat-weight-loss 80} {:meat-balance 20}]},
{ :2007-08-06 [ { :meat-weight-gain 10} {:meat-weight-loss 60} {:meat-balance -30}]},
{ :2007-08-07 [ { :meat-weight-gain 40} {:meat-weight-loss 80} {:meat-balance -70}]}
{ :2007-08-08 [ { :meat-weight-gain 100} {:meat-weight-loss 0} {:meat-balance 30}]}])
Create a new data structure:
(defn turner [stats]
(apply merge
{:year (-> stats keys first)}
(-> stats vals first)))
(def data2 (mapv turner data))
[{:year :2007-08-05, :meat-weight-gain 100, :meat-weight-loss 80, :meat-balance 20}
{:year :2007-08-06, :meat-weight-gain 10, :meat-weight-loss 60, :meat-balance -30}
{:year :2007-08-07, :meat-weight-gain 40, :meat-weight-loss 80, :meat-balance -70}
{:year :2007-08-08, :meat-weight-gain 100, :meat-weight-loss 0, :meat-balance 30}]
Now you group your data by a predicate that check whether the balance was negative or not:
(partition-by #(-> % :meat-balance neg?) (sort-by :year data2))
(({:year :2007-08-05, :meat-weight-gain 100, :meat-weight-loss 80, :meat-balance 20})
({:year :2007-08-06, :meat-weight-gain 10, :meat-weight-loss 60, :meat-balance -30}
{:year :2007-08-07, :meat-weight-gain 40, :meat-weight-loss 80, :meat-balance -70})
({:year :2007-08-08, :meat-weight-gain 100, :meat-weight-loss 0, :meat-balance 30}))
Let it be data3. Then, filter that data structure to get only negative ones:
(filter #(-> % first :meat-balance neg?) data3)
(({:year :2007-08-06, :meat-weight-gain 10, :meat-weight-loss 60, :meat-balance -30}
{:year :2007-08-07, :meat-weight-gain 40, :meat-weight-loss 80, :meat-balance -70}))
Let it be data4. Now you get the boundaries:
{:period-start (-> data4 first first :year)
:period-end (-> data4 first last :year)
:end-period-balance (-> data4 first last :meat-balance)}
what gives you exactly
{:period-start :2007-08-06,
:period-end :2007-08-07,
:end-period-balance -70}

First of all the complex input data structure can be disentangled:
(map (juxt ffirst (comp first #(keep :meat-balance %) val first)))
;;=> ([:2007-08-05 20] [:2007-08-06 -30] [:2007-08-07 -70] [:2007-08-08 30])
... into tuples of [date-keyword meat-balance].
Notice that so far we are keeping both positive and negative meat balances. The answer requires negative runs i.e. contiguous negative meat balances. partition-by is the go to function for any kind of run, after which we can filter to get only the partitioned groups required for the answer. And before anything we need to sort because your date keys were originally in a map and maps are unsorted. After sorting, partitioning and filtering we are ready to deliver the answer, which simply entails transforming our canonical [date-keyword meat-balance] data structure into the required structure:
(->> data
(map (juxt ffirst (comp first #(keep :meat-balance %) val first)))
(sort-by first)
(partition-by #(-> % second neg?))
(filter #(-> % first second neg?))
(map (fn [neg-run]
(let [[start-date _] (first neg-run)
[end-date end-value] (last neg-run)]
{:period-start start-date
:period-end end-date
:end-period-balance end-value})))
;;=> [{:end-period-balance -70, :period-start 2007-08-06, :period-end 2007-08-07 }]

Related

How to "filter" maps properly in Clojure?

I ve been playing around w/ Clojure for a while and I got stuck in something I think very trivial for many...but not me. I've the following piece of code;
;; Define a Record structure
(defrecord Person [first-name last-name age occupation])
(def john (->Person "John" "Frusciante" 50 "Guitarist"))
;; People map
(def people {"1" john
"2" (->Person "Pablo" "Neruda" 90 "Poet")
"3" (->Person "Stefan" "Zweig" 120 "Author")
}
)
(defn get-120-year-old-guy
[peeps]
(filter #(= (:age %) 120) peeps)
)
(println "who's 120?: " (get-120-year-old-guy people))
This call returns an empty list. I know it's something wrong the way I retrieve the value but can't see what is that exactly.
You can get a hint of what's going on by changing the function temporarily:
(defn get-120-year-old-guy
[peeps]
(filter (fn [m] (println (type m) m)) peeps))
Prints:
(clojure.lang.MapEntry [1 #user.Person{:first-name John, :last-name Frusciante, :age 50, :occupation Guitarist}]
clojure.lang.MapEntry [2 #user.Person{:first-name Pablo, :last-name Neruda, :age 90, :occupation Poet}]
clojure.lang.MapEntry [3 #user.Person{:first-name Stefan, :last-name Zweig, :age 120, :occupation Author}]
)
Note how each entry is a MapEntry. In your attempt, you're applying :age to the whole MapEntry (which returns nil), not just the person.
I think destructuring using a full anonymous function would be the easiest way:
(defn get-120-year-old-guy
[peeps]
(filter (fn [[_ person]] (= (:age person) 120)) peeps))
Outputs:
who's 120?: ([3 #user.Person{:first-name Stefan, :last-name Zweig, :age 120, :occupation Author}])
#leetwinski points out a more idiomatic solution that does away with the explicit function altogether:
(filter (comp #{120} :age val) people)
Broken down:
(defn get-120-year-old-guy [peeps]
(filter (comp ; And (comp)ose all three checks together
#{120} ; Then test if it's in the set of #{120}
:age ; Then get the age
val) ; Get the value from the MapEntry
peeps))
If you look at the first item in your outer map, you'll see that each item is a clojure.lang.MapEntry from string to Person. The key is "1" and the value is a Person record:
> (first people)
["1"
{:first-name "John",
:last-name "Frusciante",
:age 50,
:occupation "Guitarist"}]
To filter on the :age field, you have to first get the value of the {key, value} pair. One way is to use the val function to get that before getting :age from the Person map. Your filter function is then:
(defn get-120-year-old-guy
[peeps]
(filter #(= (:age (val %)) 120) peeps)
)
> (println "who's 120?: " (get-120-year-old-guy people))
who's 120?: ([3 #challenges.anag.Person{:first-name Stefan, :last-name Zweig, :age 120, :occupation Author}])
Another option is to destructure the clojure.lang.MapEntry:
(defn get-120-year-old-guy
[peeps]
(filter (fn [[_ v]] (= (:age v) 120)) peeps)
)
Here you can see (fn [[_ v]] ... where the _ is used as a place holder for the unused key.

Clojure; How to iterate through a vector of maps, so that the index is known, to be able to update-in the map?

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.

Clojure: How to apply a function to every value in a nested map and update?

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)))

Creating a map from a var in Clojure

How does one create a map from a var in Clojure?
For example, if one has a var called 'var', as follows:
(def var "I am a var")
And then, one wants to convert it to the following:
{:var "I am a var"}
How does one achieve that?
By the way, just to be clear:
I want the f from (f var) where 'f' converts var to {:var "I am a var"}.
If you want to create a map from the var name to its value you can create a macro:
(defmacro to-map [sym]
`{(keyword '~sym) ~sym})
(to-map var)
=> {:var "I am a var"}
FYI, this macro can take optional args to construct a map.
(defmacro to-map [& vs]
"foo = 1, bar = 2. (to-map foo bar) ==> {:foo 1 :bar 2}"
`(let [ks# (map keyword '~vs)]
(zipmap ks# [~#vs])))
maybe you can do if using the var's metadata:
(defn process-var [param]
{(-> param meta :name keyword)
#param})
in repl:
user> (def x "fourty two")
#'user/x
user> (process-var #'x)
{:x "fourty two"}
user> (def y 1001)
#'user/y
user> (map (comp process-var (ns-interns *ns*)) '(x y))
({:x "fourty two"} {:y 1001})
user> (map (comp process-var (ns-interns *ns*)) '(x y process-var))
({:x "fourty two"} {:y 1001} {:process-var #function[user/process-var]})
also you can take a var straight from namespace's symbols table:
(defn by-name
([name-symbol] (by-name name-symbol *ns*))
([name-symbol ns]
{(keyword name-symbol)
(deref (ns-resolve ns name-symbol))}))
user> (by-name 'x)
{:x "fourty two"}
user> (by-name 'map 'clojure.core)
{:map #function[clojure.core/map]}
user> (by-name '*clojure-version* 'clojure.core)
{:*clojure-version* {:major 1, :minor 8, :incremental 0, :qualifier nil}}

Explanation of specific part in a diagram

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.

Resources