So... boooored. I might as well type up whatever I know about destructuring, which is... extremely, EXTREMELY useful, to say the least.
In fact, I know quite a bit about how to use it (not everything, though), but nothing about how it works under the hood. I only recently learned how lazy-seqs work (which, incidentally, is an interesting topic for another time).
So, destructuring.
If you recall, in my previous post I used something along the lines of
(defn reroot [tree [n1 & nr]] ...
Aaaand, here's destructuring. Instead of giving a second name for the second argument, I put [n1 & nr]. The second argument passed will be destructured into n1 and nr, where n1 holds the first element of that vector, and nr the other elements (that's what &, the rest argument is for). The funny thing is, this kind of destructuring can be arbitrarlily nested, which is powerful.
Really.
More examples:
(let [[a b c] [1 2 3]]
(println a b c))
-> "1 2 3"
(let [[a b & c] [1 2 3]]
(println a b c))
-> "1 2 [3]"
(let [[[a & b] & c] [[1 2 3 4] 5 6 7 8]]
(println a b c))
-> "1 [2 3 4] [5 6 7 8]"
I'm currently writing a game, and I have a map that holds the entire game state. Pure functions act on that map, returning a new one with changes to the game world incorporated. There are many uses for destructuring there.
The world is made up of tiles. I use a map of vectors to maps for those tiles; the vector has two elements (x and y position), and the maps holds all data about the given tile. Now, when a map is ran through (seq), it returns a seq of two-element vectors - the first element is the key, the second one the value. Hence when drawing the field, I can do this:
(doseq [[[x y] tile] tiles]
...)
Yes, destructuring can be used in doseq and for. Even in let!
See, I destructured the input vector (which is, remember, a key-value pair) into [x y] and tile. Then, the first element is further destructured into x and y. That way I have access to all those important informations without having to manually extract them.
And this is destructuring for vectors only. Yes, there are other kinds.
But before I go into them, let me note one thing first. If, via destructuring, a name should be bound to some element, but there is none, those names are bound to nil:
(let [[a b c] [1 2]]
(println a "/" b "/" c))
-> "1 / 2 / nil"
Alright. Destructuring MAPS. This is fun as well. See, in my game I rely on maps a lot. Units, for example, are maps holding all necessary information. In many functions, I don't need all of these informations, so some handy way to extract the ones I DO need would be... handy.
Behold.
(def unit
{ :power 1 :endurance 3 :name "Thing" :image nil :owner 1 :range 3 :position [2 2] })
(let [{name :name, owner :owner} unit]
(println name "belongs to player" owner))
-> "Thing belongs to player 1"
And you can mix both types of destructuring.
(let [{name :name, [x y] :position} unit]
(println name "is on field" x y))
-> "Thing is on field 2 2"
Again; if a key is not found in the map, nil is bound to the corresponding names.
So. Destructuring is awesome.
Words of warning, though. Overusing this makes code unreadable. Seriously. I usually try to avoid using this in defn parameter vectors, unless the function is trivial, but it really shines in for and doseq, and has it's uses in lets. I rely on it a lot to make those parts more concise.
No comments:
Post a Comment