Search a sequence for its minimum and stop as soon as the lowest possible value is found
Peter Otten
__peter__ at web.de
Sat Jan 7 06:55:12 EST 2017
Steve D'Aprano wrote:
> On Sat, 7 Jan 2017 01:04 am, Peter Otten wrote:
>
>> Example: you are looking for the minimum absolute value in a series of
>> integers. As soon as you encounter the first 0 it's unnecessary extra
>> work to check the remaining values, but the builtin min() will continue.
>>
>> The solution is a minimum function that allows the user to specify a stop
>> value:
>>
>>>>> from itertools import count, chain
>>>>> stopmin(chain(reversed(range(10)), count()), key=abs, stop=0)
>> 0
>>
>> How would you implement stopmin()?
>
> That depends on whether you want to stop when you find a value *equal* to
> the stop value, or *less than* the stop value, or both.
>
> It depends on whether you want to check for equality or check for any
> arbitrary condition.
>
> It depends on whether you want the stop value to be included or not. (It
> looks like you do want it included.)
>
> But I'd start with this:
>
>
> # not tested
> def stopmin(iterable, key=None, stop=None):
> it = iter(iterable)
> try:
> smallest = next(it)
> except StopIteration:
> raise ValueError('empty iterable has no minimum')
> else:
> if key is not None:
> keyed_smallest = key(smallest)
Must test for stop here.
> if key is None:
> for x in iterable:
Replace `iterable` with `it` in the for loops.
> if x < smallest:
> smallest = x
> if x == stop:
> break
> else:
> for x in iterable:
> y = key(x)
> if y < keyed_smallest:
> keyed_smallest = y
> smallest = x
> if y == stop:
> break
> return smallest
The whole distraction started when I wanted to avoid the straight-forward
thing ;)
As it turns out there is not as much legwork as I expected, particularly as
I'm not interested in the no-key branch.
> Another possibility is to create a variant of itertools takewhile:
>
> def takeuntil(predicate, iterable):
> # takeuntil(lambda x: x<5, [1,4,6,4,1]) --> 1 4 6
> for x in iterable:
> yield x
> if predicate(x):
> break
>
> min(takeuntil(lambda x: x == 0, iterable), key=abs)
>
>
> py> from itertools import count, chain
> py> iterable = chain(reversed(range(10)), count())
> py> min(takeuntil(lambda x: x == 0, iterable), key=abs)
> 0
> py> iterable = chain(range(-9, 10), count())
> py> min(takeuntil(lambda x: x == 0, iterable), key=abs)
> 0
More information about the Python-list
mailing list