Or: Writing Goals with Pattern Matching
I’d been looking for an excuse to use core.logic for something, and I finally created an opportunity when I wrote a Secret Santa picker.
(Familiarize yourself with this primer for background on how core.logic works and looks.)
Zipping lists
At the end of running a Secret Santa program, you effectively want a map of the people involved to the people they’re giving gifts to. Unfortunately, working with maps isn’t straightforward in core.logic, but lists are. I figured I’d take a list of participants and a list of recipients and zip them together.
1 2 3 4 5 6 7 |
|
Like conso
, firsto
, and resto
, this is a goal, so the “result”
of zipping the first two arguments must be supplied as well.
In the first case (line 4), we’re saying that two empty lists zipped
together is the empty list. In the second case (line 5), we
destructure the lists into their heads and tails, saying that the
head (z
) is the vector [x y]
, and that the tails have the same
zipped relationship.
I find that running goals on non-grounded vars can be illuminating:
1 2 3 4 5 |
|
But here are some regular sanity checks:
1 2 3 4 |
|
That’s working. It zips, or unzips, depending on where you query.
Preventing someone from buying their own gift
We can’t just take our list of names, generate a permutation of them
(with permuteo
) and zip those together:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Everyone’s buying for themselves! We’ll need to add a constraint against that:
1 2 3 4 5 6 7 |
|
Once again, pattern matching. The degenerate case, the empty list (line 4), simply succeeds. If we have a head and tail (line 5), we destructure the head, too, so we can assert that the two members of that vector pair are not equal.
This gets us a solution that works:
1 2 3 4 5 6 7 8 9 10 11 |
|
But it still might not be ideal.
Prohibiting certain pairs
It may be necessary (for personal or HR reasons) that some people shouldn’t give gifts to certain others.
1 2 3 4 5 6 7 8 |
|
Hopefully, this pattern-matching pattern is clear now.
And now we can add further constraints:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Doing that with slips of paper in a hat would be tiresome.
This code (plus some helper functions) is on github.