Here’s mine. Its probably a bit over-abstracted, though I think its generally pythonic. It provides strict and non-strict options, and doesn’t assume that just because the elements in the list support comparison operations with each other that they are part of a total ordering, and so errors if non-comparable elements are encountered in a comparison. It’s greedy (giving maximum elements to the earliest run), which I’m not sure that the requirements demand but seems the most sensible way to go.
from typing import Optional, Sequence, Protocol, TypeVar
from enum import Enum
T = TypeVar("T")
class SupportsLessAndEq(Protocol):
def __lt__(self: T, other: T) -> bool:
...
def __eq__(self: T, other: T) -> bool:
...
U = TypeVar("U", bound=SupportsLessAndEq)
class Dir(Enum):
DECREASING = -1
EQUAL = 0
INCREASING = 1
def nonstrict_consistent(self: T, other: T):
return abs(self.value-other.value) Optional[Dir]:
return Dir.INCREASING if x Dir:
result = try_cmp(x,y)
if result is None:
raise ValueError(f"Cannot compare {repr(x)} with {repr(y)}")
return result
def strict_consistent_direction(base_direction: Dir, new_direction: Dir):
return base_direction if (base_direction == new_direction) else None
def nonstrict_consistent_direction(base_direction: Dir, new_direction: Dir):
return (base_direction or new_direction) if base_direction.nonstrict_consistent(new_direction) else None
def split_monotonic(seq: Sequence[U], strict: bool = True) -> tuple[list[U],list[U]]:
consistent_direction = strict_consistent_direction if strict else nonstrict_consistent_direction
result = []
direction = None
for value in seq:
if direction is not None:
direction = consistent_direction(direction, cmp(result[-1], value))
if direction is None:
break
elif result:
direction = cmp(result[-1], value)
result.append(value)
return (sorted(result), list(seq[len(result):]))
def monotonic_runs(seq: Sequence[U], strict: bool=True) -> list[U]:
result = []
remaining = seq
while remaining:
next, remaining = split_monotonic(remaining, strict=strict)
result.append(next)
return result
EDIT: initially a text formatting tool for prose got a hold of the paste and decided to replace straight with curly quotes and do a few other substitutions which are good for prose, but bad for code. I think I cleaned them all up now.