We wish to count the teeth in a gear train represented as an ordered list
of ordered triples. Each triple represents a gear cluster, and consists
of an identifier, an ordered list of integers (the "numerator" gears), and
an equally long list of "denominator" gears. If the last n numerator gears
coincide with the last n of some later cluster, then the last n+1 denominator
gears coincide also, and these two clusters share those gears, and we want to
count their teeth only once.
(defun teeth (train)
(loop for clusters on train
sum (+ (toot clusters #'second) (toot clusters #'third))))
(defun toot (clusters accessor
&aux (gears (funcall accessor (first clusters))))
(if (or (null gears)
(some #'(lambda (cluster)
(loop for cogs on (funcall accessor cluster)
when (equal gears cogs) return 't))
(rest clusters)))
0
(+ (first gears) (toot clusters #'(lambda (cluster)
(cdr (funcall accessor cluster)))))))
toot counts the numerator or denominator teeth of a cluster, depending
on the accessor. When it finds an unshared gear, it adds it to the
count of the rest of its list. But the list per se is not a transmitted
argument on which to recurse. So we put the CDR in accessor! Termination
is not running out of clusters, but rather running out of gears in the
first cluster.
--rwg