Skip to content

Instantly share code, notes, and snippets.

@toomasv
Last active April 24, 2018 09:18
Show Gist options
  • Save toomasv/1b1d8e859eeb1acfa230cc5357ca12fa to your computer and use it in GitHub Desktop.
Save toomasv/1b1d8e859eeb1acfa230cc5357ca12fa to your computer and use it in GitHub Desktop.
Range for on-demand evaluation
Red [
Author: "Toomas Vooglaid"
Date: 27-11-2017
]
lazy-range: function [
"Returns function that generates next value in (possibly infinite) range"
val [scalar!] "Initial value"
/step stp [scalar! block!] "By which to increment each next value"
/limit lim [integer!] "How many new values to generate"
/stop stopval [scalar! block!] "On which value to stop generation of new values; if block, then also after how may matches"
/circle crcl [scalar!] "To generate (possibly infinite) new values in limited range"
][
cx: context compose/deep [
limit: lim
stop: (either all [stop block? stopval][stopval/1][stopval])
step: (either step [([stp])][if percent? val [1%][1]])
val: (val)
count: 0
val2: none
cval: none
inc: if block? step [get step/2] ;either block? step [get step/2][:+]
circle: crcl
stop-count: (if all [stop block? stopval][stopval/2])
count-stop: 0
count-circle: 0
bstep: if block? step [either '- = step/2 [0 - step/3][step/3]]
b2stp: 0
next-val: does [
if any [limit circle][count: count + 1]
either any [
all [limit limit < count]
all [
stop
either circle [
if all [stop-count stop = val2][count-stop: count-stop + 1]
and~ stop = val2 either stop-count [stop-count = count-stop][true]
][
pick reduce [stop > val stop < val] either any [
all [number? step negative? step]
all [block? step '- = step/2]
][true][false]
]
]
][none][
either circle [
cval: either block? step [
either number? circle [
val2: val
b2stp: count - 1 * bstep % circle
val2/(step/1): val2/(step/1) + b2stp
val2
][cause-error 'user 'message ["Only integer! type supported for circle in case of block step!"]]
][
val + case [
or~ number? circle time? circle [count - 1 * step % circle]
true [cause-error 'user 'message ["Only numeric and time types supported for circle!"]]
]
]
][
also val val: either block? step [
val2: val
val2/(step/1): val2/(step/1) inc step/3
val2
][
val + step
]
]
]
]
]
:cx/next-val
]
@toomasv
Copy link
Author

toomasv commented Nov 26, 2017

Examples

Simple generator:

>> get-next: lazy-range 1
[...]
>> get-next
== 1
>> get-next
== 2
>> get-next
== 3

In loops:

>> get-next: lazy-range 1-1-2017
[...]
>> loop 5 [probe get-next]
1-Jan-2017
2-Jan-2017
3-Jan-2017
4-Jan-2017
5-Jan-2017

With /limit:

>> get-next: lazy-range/limit -5 5
[...]
>> while [n: get-next][probe n]
-5
-4
-3
-2
-1

With /step:

>> get-next: lazy-range/step/limit 12:00 0:15 5
[...]
>> while [n: get-next][probe n]
12:00:00
12:15:00
12:30:00
12:45:00
13:00:00

With /stop:

>> get-next: lazy-range/stop 12 20
[...]
>> collect [while [n: get-next][keep n]]
== [12 13 14 15 16 17 18 19 20]

With /circle:

>> next-val: lazy-range/circle 1x2 3
[...]
>> next-val
== 1x2
>> next-val
== 2x3
>> next-val
== 3x4
>> next-val
== 1x2
>> next-val: lazy-range/step/circle 1 2 5
[...]
>> loop 10 [probe next-val]
1
3
5
2
4
1
3
5
2
4
>> next-val: lazy-range/step/circle 1-1-2017 2 5
[...]
>> loop 6 [probe next-val]
1-Jan-2017
3-Jan-2017
5-Jan-2017
2-Jan-2017
4-Jan-2017
1-Jan-2017

When stop is block then second value is interpreted as the number of matches before stopping, e.g. in following 1-Jan-2017 is matched 3 times before stopping.

>> next-val: lazy-range/step/circle/stop 1-1-2017 [month + 2] 5 [1-1-2017 3]
[...]
>> collect [while [n: next-val][keep n]]
== [1-Jan-2017 1-Mar-2017 1-May-2017 1-Feb-2017 1-Apr-2017 1-Jan-2017 1-Mar-2017 1-May-2017 1-Feb-2017 1-Apr-2017 1-Jan-2017]
>> next-val: lazy-range/step/circle/stop 1-1-2017 [month - 2] 5 [1-1-2017 3]
[...]
>> collect [while [n: next-val][keep n]]
== [1-Jan-2017 1-Nov-2016 1-Sep-2016 1-Dec-2016 1-Oct-2016 1-Jan-2017 1-Nov-2016 1-Sep-2016 1-Dec-2016 1-Oct-2016 1-Jan-2017]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment