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.