One more from Programming Praxis, this time we’re dealing with summing combinations of a sequence. More formally, given a secquence S, either choose four elements s_1 through s_4 from S such that s_1 + s_2 + s_3 + s_4 = 0 or verify that it isn’t possible. This immediately makes me about working through possible solutions until we find one and then bailing out, ergo call/cc:
(define (4sum ls)
(call/cc (lambda (exit)
(for-each (lambda (i)
(for-each (lambda (j)
(for-each (lambda (k)
(for-each (lambda (l)
(when (= 0 (+ i j l k))
(exit (list i j k l))))
ls))
ls))
ls))
ls)
(exit #f))))
It may look funny, but it works great:
~ (4sum '(2 3 1 0 -4 -1))
(2 2 0 -4)
~ (4sum '(3 1 0 -4))
(3 1 0 -4)
~ (4sum '(1 1 1 1))
#f
I think that this may have been the first time that I legitimately used call/cc without being told to do so and I have to admit… I’m a convert. It’s kind of powerful. (Even if I’m not using a fraction of what I’m capable of.)
Alternatively, we can write a more Rackety solution, using for*/first
to loop over the values and return the first matching case:
(define (4sum ls)
(for*/first ([i (in-list ls)]
[j (in-list ls)]
[k (in-list ls)]
[l (in-list ls)]
#:when (= 0 (+ i j k l)))
(list i j k l)))
Under the hood, it’s doing much the same thing, so it really comes down to which style looks more natural to you.