Pythonic monotonic
I like how the first one uses groupby() but I don&#x27;t like the definition of &quot;Monotonic&quot; inside of the module, nor the name, nor returning a list instead of a generator. I also prefer functions instead of callable instances.<p>One alternative to use a function closure instead of a class:<p><pre><code>    import math
    import itertools

    def compare_with_previous():
        prev = -math.inf
        def compare(value):
            nonlocal prev
            test = prev &lt; value
            prev = value
            return test
        return compare

    def monotonic(seq):
        for is_increasing, group in itertools.groupby(seq, compare_with_previous()):
            group = list(group)
            if not is_increasing:
                group.reverse()
            yield group
            
    print(list(monotonic([1, 2, 3, 2, 1, 4, 5, 6, 7])))
</code></pre>
Another is to be more itertools oriented:<p><pre><code>    import math
    import itertools
    
    # Given a pair (a, b), test if a &lt; b
    def lt_pair(pair):
        return pair[0] &lt; pair[1]

    def monotonic(seq):
        # iterate through a two-element window of the sequence, using -inf as the first term
        seq1, seq2 = itertools.tee(seq)
        window = zip(itertools.chain([-math.inf], seq1), seq2)

        # group by increasing or decreasing (arbitrarily assume equal means increasing)
        for is_increasing, group in itertools.groupby(window, lt_pair):
            if not is_increasing:
                # reverse the direction
                group = list(group)[::-1]
            yield [pair[1] for pair in group]
            
    print(list(monotonic([1, 2, 3, 2, 1, 4, 5, 6, 7])))
</code></pre>
NOTE! None of these solutions support monotonicity correctly for equal values!<p><pre><code>  &gt;&gt;&gt; print(list(monotonic([1, 1, 2, 3, 3, 2, 1, 1])))
  [[1], [1], [2, 3], [1, 1, 2, 3]]
  &gt;&gt;&gt; list(mono_runs_simpler([1, 1, 2, 3, 3, 2, 1, 1]))
  [[1], [1], [2, 3], [1, 2, 3], [1]]
</code></pre>
That should be (IMO):<p><pre><code>  [[1, 1, 2, 3, 3], [2, 1, 1]]
</code></pre>
as the term &quot;strictly monotonic&quot; is for the always-increasing or always-decreasing.<p>Handling this case is much easier in the first of these two implementations:<p><pre><code>    import math
    import itertools

    def monotonic_comparison():
        prev_value = -math.inf
        prev_test = True
        def compare(value):
            nonlocal prev_value, prev_test
            if prev_test:
                # Testing for monotonic increasing
                test = prev_value &lt;= value
            else:
                # Testing for monotonic decreasing
                test = prev_value &lt; value
            prev_value = value
            prev_test = test
            return test
        
        return compare

    def monotonic(seq):
        for is_increasing, group in itertools.groupby(seq, monotonic_comparison()):
            group = list(group)
            if not is_increasing:
                group.reverse()
            yield group
</code></pre>
HOWEVER! At this point it&#x27;s a lot of work structured around using groupby(). It&#x27;s more readable, as Ned Batchelder points out, to just write the code directly, without groupby() or nonlocal:<p><pre><code>    import math
    def monotonic_direct(seq):
        prev_value = -math.inf
        prev_test = True
        group = []
        for value in seq:
            if prev_test:
                test = prev_value &lt;= value
            else:
                test = prev_value &lt; value

            if test == prev_test:
                group.append(value)
            else:
                if not prev_test:
                    group.reverse()
                yield group
                group = [value]
            prev_value = value
            prev_test = test

        if group:
            yield group

  &gt;&gt;&gt; print(list(monotonic_direct([1, 2, 3, 2, 1, 4, 5, 6, 7])))
  [[1, 2, 3], [1, 2], [4, 5, 6, 7]]
  &gt;&gt;&gt; print(list(monotonic_direct([1, 1, 2, 3, 3, 2, 1, 1])))
  [[1, 1, 2, 3, 3], [2, 1, 1]]
 </code></pre>
(I&#x27;m using -inf as a special sentinel, instead of special-casing the [0] element but I think that&#x27;s a minor point.)<p>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:<p><pre><code>  &gt;&gt;&gt; list(monotonic_direct([10, 9, 8]))
  [[10], [9, 8]]
</code></pre>
As such, the original reference &quot;Monotonic&quot; implementation is wrong, as well as Ned&#x27;s version!<p><pre><code>  &gt;&gt;&gt; list(mono_runs_simpler([10, 9, 8]))
  [[10], [9, 8]]
</code></pre>
It should be [[10, 9, 8]].