Edit: I believe the bug is that the code assumes the first sequence is increasing. It seems a bug carried by all these variants that starts by declaring a -inf variable at the top.
IMO, the problem is trying to find a solution in one's head, then trying to write Pythonic code. The issue is that when someone else reads the code, one does not have the unwritten idea you had in your head.
To write simple code, you must have the least amount of idea in your head. I usually try to write the code dumbly, even if it is inefficient, then do a second pass to remove the most offending parts and maybe make the code more general.
I find myself running into the same limitation sometimes-- functools/itertools are only so flexible.
As is, my implementation requires objects where "<" and "<=" are meaningful, and it requires that a comparison to -math.inf is also meaningful.
For example, it won't work with complex numbers, first because they cannot be compared to -math.inf:
>>> list(monotonic_direct([1j, 2j, 4j]))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "monotonic.py", line 40, in monotonic_direct
test = prev_value <= value
TypeError: '<=' not supported between instances of 'float' and 'complex'
and second because Python doesn't support an ordering for complex numbers: >>> 1j < 2j
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'complex' and 'complex'
As I mentioned, it's possible to replace the sentinel -math.inf with something that starts with seq[0]. This would allow strings and orderable objects.This is left as an exercise for the reader. ;)
You might be able to create an object that always compares as less than something, using something like:
class Bottom:
def __le__(self, other):
return True
def __lt__(self, other):
return True
though use this with care.
One alternative to use a function closure instead of a class:
Another is to be more itertools oriented: NOTE! None of these solutions support monotonicity correctly for equal values! That should be (IMO): as the term "strictly monotonic" is for the always-increasing or always-decreasing.Handling this case is much easier in the first of these two implementations:
HOWEVER! At this point it's a lot of work structured around using groupby(). It's more readable, as Ned Batchelder points out, to just write the code directly, without groupby() or nonlocal: (I'm using -inf as a special sentinel, instead of special-casing the [0] element but I think that's a minor point.)EDIT: I am wrong. The initial -inf means the first term is always assumed to be in an increasing subsequence, even if it is part of a decreasing subsequence:
As such, the original reference "Monotonic" implementation is wrong, as well as Ned's version! It should be [[10, 9, 8]].