Breaking "Functional Loops" and Doing Lazy Evaluation In Mathematica

In my lazyLists package mentioned by the OP, you would do something like this to find out if a list is monotonic:

<< lazyLists`
n = 100000;
(* lazy representation of the example input *)
input = lazyCatenate[{{3, 4, 2}, lazyGenerator[# &, 1, 1, n, 1]}];
monotonicQ[lz_lazyList, test_] := Catch[
 FoldList[
   If[TrueQ @ test[#2, #1], #2, Throw[False, "nonmonotonic"]]&,
   lz
 ][[-1]]; (* taking the last part iterates through the lazyList *)
 True
 ,
 "nonmonotonic"
];
monotonicQ[input, Greater]

False

You can also use partitionedLazyList to generate elements in batches, which is usually faster for long arrays.


Applying DeMorgan's law to the logic simplifies things a bit:

With[{ d = Differences[input] },
 Nand[AnyTrue[d, # < 0 &], AnyTrue[d, # > 0 &]]
]

The idiomaticâ„¢ way to solve this is with SequenceCases to report the first case where an element is smaller than the previous one:

ismontoneinc[list_] := SequenceCases[list, {x_, y_} /; y < x, 1] == {}
ismontonedec[list_] := SequenceCases[list, {x_, y_} /; y > x, 1] == {}
ismonotone[list_] := ismontoneinc[list] || ismontonedec[list]
data = {1, 2, 3, 4, 1, 6}; ismonotone[data]
(* result: False - not monotone *)

data = {1, 2, 3, 4, 5, 6, 7, 8}; ismonotone[data]
(* result: True - monotone *) 

data = {5,3,2,0}; ismonotone[data]
(* result: True - monotone *) 

However, this has hopelessly bad performance with a million random integers in v12.1.1. and terrible memory usage too. Just try ismonotone[RandomReal[1, 100000]] - it clearly doesn't even break early which is very disappointing. I guess Mathematica is full of surprises.