You can use zip
and islice
to construct an iterator with its own "future" available to it.
next(
filter(
lambda pair: isprime(pair[1]), # The predicate applied to a pair
zip(xs, islice(xs, 1, None)) # The iterator and its 'future'
),
(None, None) # A default pair in case no matches are found
)[0] # Retrieve the 'current' entry from the matching pair
At each step you have a pair that you can think of as (present, future)
and the predicate will be applied to future
. At the end, we unpack the present
, which represents the last non-matching entry in the iterator.
Note that this implementation, as presented, does not return the last entry in the list, because inherently if you zip [a, b, c]
against its own shifted version you end up with one shorter than the other. You can use zip_longest
(from itertools
) to overcome this, but you'll need to handle the fillvalue
(usually None
) in your predicate.
As noted in a comment, this does not work for generators because it will consume the generator. Using a fold (reduce
in Python), though, it becomes easier. First, in a more "Pythonic" presentation, the function looks something like this:
def fold(hist, cur):
stop, prev = hist
if stop:
return hist
if isprime(cur):
return (True, prev)
return (False, cur)
The first element of the tuple serves as a "stop" marker, and the second is our "needle." You can use this with reduce thus:
reduce(fold, xs, (False, None))[1]
That's not exactly a "one liner," but we can compress it into a lambda
:
reduce(lambda z, x: (z if z[0] else ((1, z[1]) if isprime(x) else (0, x))), xs, (0, None))[1]