This problem can be solved if you allow the kernel function of the fold to capture a lexical scope containing mutable variables. Then you can keep the accumulator type Boolean, and yet have enough state to do the calculation.
Pseudocode:
(foldl (let ([s 0])
(lambda (accum item)
;; if (equal? item 1) and s is not already 2
;; increment s
;; return (equal? s 1)
)
#f list)
You cannot solve this with a function that doesn't capture any environment; but why restrict to that? A function is code plus a lexical environment, by definition.
In the above, the accumulator is basically a dummy; we don't even look at it, because the state s
represents everything we need. We could use a Boolean s
such that the state is a combination of the accumulator param and the information in s
. We could divide the state between a Boolean accumulator and a Boolean s
(so that they together form a two-bit counter representing the necessary three states).
Here is an informal proof that it can't be solved with just a Boolean-returning function without a mutable environment:
Observe that the result has to be Boolean: is there exactly one 1
, true or false? So the function we use as the fold kernel must have a Boolean accumulator, since the accumulator is what is returned.
The accumulator of the fold encapsulates the entire state on which the kernel function's algorithm makes the decision. If, for instance, the function makes use of a lexical scope which contains mutable variables, that would be cheating.
The algorithm requires at least three states in the accumulator. The accumulator must be in some initial S0, from which it transitions to S1 when a 1
is seen, from which it transitions to S2 when another 1
is seen. This accumulator must then be interpreted outside of the fold as S0
and S2
denoting false, and S1
true.
Though we could, in theory, change the accumulator type between visited items, we have no information for this; we are not informed which element is last. If we knew that we are looking at the last element, we could lapse our tri-state accumulator to a Boolean value and return that.
This is why the second part of Sylwester's answer uses continuations: then the algorithm, instead of transitioning to S2 can escape out of the fold directly and produce a false; the accumulator can then be Boolean. (A much simpler non-local exit mechanism would suffice in place of full blown continuations, such as a return from a lexical block).