Destructuring Collections
The idea of destructuring parameters was confusing to me at first, but after seeing it used a few times, it started to make a lot more sense.
Today I revisited some of the Clojure Koans and came across the destructuring meditation.
Destructuring Collections
The most basic type of destructuring is just taking some number of values from a collection. If I wanted a function to add the first two elements, I could write something like this:
user=> (defn add-first-two [[a b]] (+ a b))
#'user/add-first-two
user=> (add-first-two [1 2])
3
user=> (add-first-two '(3 2 1))
5
Using a second set of brackets in the function’s parameter list, I can pass any collection to the function, then name and use the first two values of that collection.
Without destructuring, I’d be left with something like this:
(defn add-first-two [coll]
(+ (first coll) (second coll)))
The & Rest
of the List
So what if we have a really long list, or a list where we don’t
know the size ahead of time? Using & rest
parameters, we can
store the remainder of the list in a sublist for later use.
Here is a light version of or
using the rest parameter.
user=> (defn my-or [[next & rest]]
(if next next (my-or rest)))
#'user/my-or
user=> (my-or [nil false 1 true])
1
Using the rest parameter, we can take only the number of items
we need from the collection and store the rest
of the list in another
variable!
Though, the rest parameter doesn’t necessarily need to be named
rest
. You can call it flowers
, lightsabers
, or something that makes
a little more sense with the context of your function.
Restructuring Collections
One thing I didn’t know about destructuring is that you can have both destructured and fully structured values in your parameter list!
To elaborate a bit more… If I have a collection, [1 2 3]
, and
want to use these items both separately and jointly, 1 2 3
and [1 2 3]
,
I can do so without the extra work of breaking them down or
joining them together in the function body.
user=> (defn sum-description [[a b :as operands]]
(str "The sum of " operands " is " (+ a b)))
#'user/sum-description
user=> (sum-description [123 456])
"The sum of [123 456] is 579"
One thing to be aware of is that while the left part of :as
holds
the destructured parameters, a b
, the right part, operands
, will
hold the entirety of the parameter.
If I decided to pass more than two numbers into this function, then the result wouldn’t make much sense.
user=> (sum-description [123 456 789])
"The sum of [123 456 789] is 579"
There’s a ton more than can be done with destructuring as well, like destructuring map values! But I’ll leave that for another post.