Alright, so this is a question about the functional way to break a for/while loop. Since we're on the Mathematica SE, I'm interested in the ways a Mathematica vet would handle this, however the question is similar in spirit to this question. I am also interested in lazy evaluation in Mathematica.
For instance, consider writing an algorithm to detect whether an array is monotonic or not. How could I rewrite the algorithm below so that it
- does not check the entire array and,
- does not store the entire
input
array in memory?
n = 1000;
input = {5, 4, 3}~Join~Range[1, n];
AllTrue[Differences[input], # >= 0 &] || AllTrue[Differences[input], # <= 0 &]
In Python 3+, one way to do this is shown below. All the operations below work on an iterator level, so only the necessary elements are computed. You can test this by setting n=100000000
and compare to the algorithm above.
from itertools import chain, islice, tee
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
return zip(a, islice(b, 1, None))
def isMonotonic(iterable):
pw_iterable = pairwise(iterable)
all_increasing = all(x <= y for x, y in pw_iterable)
all_decreasing = all(x >= y for x, y in pw_iterable)
return all_decreasing or all_increasing
n = 1000
arr = chain([5,4,3], range(1, n+1)) # obviously, non-monotonic
print(isMonotonic(arr))
I hope I've made clear my broader set of questions about computations in which a loop should be allowed to terminate early and the later elements in the list need not be computed. I would love to see how this would be done in an idiomatic Mathematica way.
@xzczd's hint to look at the lazy-computations tag helped me find this related question. TL;DR: there have been a number of attempts at implementing lazy functionality. These two appear to be the most up-to-date:
- lazyLists package
- Streaming package (doesn't appear to be actively maintained, but a comment in 2019 by L. Shifrin reports it may get more attention); see an introductory post here