I would like to use Clojure hashmaps to define generalized vectors. In this picture, the hashmap {:x 3 :y 2 :z (- 3)} represents the symbolic expression 3x + 2y - 3z.
I would like a function gen+ which acts as an addition operation on hashmaps, and which satisfies the following constraints:
- Applying gen+ to two hashmaps with overlapping keys returns a hashmap with those key values summed. e.g.,
(gen+ {:x 1} {:x 2})
;= {:x 3}
- Applying gen+ to two hashmaps with distinct keys returns a hashmap with those key-values combined. e.g.,
(gen+ {:x 1} {:y 2})
;= {:x 1 :y 2}
- The empty hashmap {} is an additive identity for the gen+ function. e.g.,
(gen+ {:x 1} {})
:= {:x 1}
- By the above constraints, any hashmap with zero entries is also an additive identity. For example, {:z 0}. Owing to this redundancy, the gen+ function should always return hashmaps without any 0 values. e.g.,
(gen+ {:x 1 :y 0} {:z 0})
;= {:x 1}
- Clojure interprets missing keys as having value nil, rather than 0, as with
({:x 3} :y) ;= nil
. Consequently, gen+ should treat nil values the same way as 0. e.g.,
(gen+ {:x 1 :y 0} {:x nil :y nil})
{:x 1}
- My Question: How can we write a function gen+ which satisfies the above constraints? Once this is in place, is it possible to overload the + operator with the gen+ function, enabling elementary addition with hashmaps?
It should be apparent why this formalism treats hashmaps as generalized vectors. Clojure interprets a vector like [x0 x1 x2] almost the same as the hashmap {:0 x0 :1 x1 :2 x2}, with a slight difference that I don't quite understand. Consequently, if we apply gen+ to two vectors, then it should be effectively the same as + applied to them. This empowers us to easily work with sparse vectors, as well as add vectors of different sizes. e.g.,
(gen+ [0 0 0 4] [0 0 0 0 0 0 0 0 0 9])
;= {:4 4 :9 9}
Here's what I don't understand about hashmaps and vectors. If I call a hashmap as a function, I need to apply a key argument like :2. On the other hand, if I call a vector as a function, I need to apply an index argument like 2. e.g.,
({:2 2} :2)
;= 2
([0 1 2] 2]
;= 2
Even if you can't help with the gen+ function, could you please explain why hashmaps and vectors behave differently when called as functions?