Skip to content

Instantly share code, notes, and snippets.

@toomasv
Last active June 19, 2021 14:41
Show Gist options
  • Save toomasv/0e3244375afbedce89b3719c8be7eac0 to your computer and use it in GitHub Desktop.
Save toomasv/0e3244375afbedce89b3719c8be7eac0 to your computer and use it in GitHub Desktop.
Range function for multiple datatypes
Red [
Author: "Toomas Vooglaid"
Date: 26-11-2017
]
range: function [val1 val2 /step stp /limit /enbase ebase /debase dbase /unicode][
stp+?: none
stp: any [stp either any [percent? val1 percent? val2] [1%][1]]
if any [all [block? stp zero? stp/3] zero? stp] [return none]
case [
debase [
unless binary? val1 [
either any-string? val1 [
val1: debase/base form val1 dbase
][
cause-error 'user 'message ["Only `any-string!` will be debased!"]
]
]
unless limit or binary? val2 [val2: debase/base val2 dbase]
]
enbase [
unless binary? val1 [val1: debase/base val1 ebase]
unless limit or binary? val2 [val2: debase/base val2 ebase]
]
]
if binary? val1 [
val1: to-integer val1 val2: to-integer val2
binary: true
ebase: any [ebase 16]
]
if all [
block? stp
any [date? val1 time? val1]
][
if negative? stp/3 [
cause-error 'user 'message ["Negative values not allowed in block step!"]
]
stp+?: either find stp '- [false][true]
]
if limit [
val2: either block? stp [
val: val1
val/(stp/1): val/(stp/1) + (val2 - 1 * pick reduce [stp/3 0 - stp/3] stp+?)
val
][
val1 + (val2 - 1 * stp)
]
]
rng: to-block case [
stp+? [pick reduce [val1 val2] stp+?]
(number? stp) or (time? stp) [pick reduce [val1 val2] stp+?: stp > 0]
true [stp+?: true val1]
]
comp: pick reduce [:<= :>=] (growing?: val1 < val2) xor stp+?
inc: pick reduce [:+ :-] either block? stp [growing? = stp+?][growing?]
addnext: pick reduce [:append :insert] stp+?
changing: pick reduce [val1 val2] stp+?
limit: pick reduce [val2 val1] stp+?
either block? stp [
while [
changing/(stp/1): changing/(stp/1) inc stp/3
limit comp changing
][
head addnext rng changing
]
][
while [limit comp changing: changing inc stp] [
head addnext rng changing
]
]
case [
binary [forall rng [
rng/1: either enbase [
system/words/enbase/base to-binary rng/1 ebase
][ to-binary rng/1]
]]
unicode [forall rng [to-char rng/1]]
]
rng
]
@toomasv
Copy link
Author

toomasv commented Nov 26, 2017

Examples

a) from down up

>> range 1 5
== [1 2 3 4 5]
>> range -4 3
== [-4 -3 -2 -1 0 1 2 3]

b) from up down

>> range 5 1
== [5 4 3 2 1]

In case of percents default step is 1%

>> range -5% 5% 
== [-5% -4% -3% -2% -1% 3.469446951954e-16% 1% 2% 3% 4%]

-3.469446951954e-16% should be reckognized as 0% :)
Percents can be mixed with floats and integers

>> range 3% -.02 
== [3% 2% 1% -3.469446951954e-16% -1%]
>> range 90% 1
== [90% 91% 92% 93% 94% 95% 96% 97% 98% 99% 100%]
>> range 1 90%
== [1 0.99 0.98 0.97 0.96 0.95 0.94 0.93 0.92 0.91]

For other datatypes (i.e. beside percent) default step is 1

>> range #"a" #"f"
== [#"a" #"b" #"c" #"d" #"e" #"f"]
>> range 1.2.3 4.5.6
== [1.2.3 2.3.4 3.4.5 4.5.6]
>> range 10x10 5x5
== [10x10 9x9 8x8 7x7 6x6 5x5]
>> range 2:00:00 2:00:05
== [2:00:00 2:00:01 2:00:02 2:00:03 2:00:04 2:00:05]
>> range 1-1-2017 5-1-2017
== [1-Jan-2017 2-Jan-2017 3-Jan-2017 4-Jan-2017 5-Jan-2017]

Steps

>> range/step 5 10 2
== [5 7 9]

Positive steps work in both directions starting on val1

>> range/step 10 5 2
== [10 8 6]

Negative numeric steps end on val2

>> range/step 5 10 -2
== [6 8 10]

For time and date recognized attributes may be used in block-steps

>> range/step 12:00 15:05 [hour + 2]
== [12:00:00 14:00:00]

Decrementing values work as negative numbers

>> range/step 12:00 15:05 [hour - 2]
== [13:05:00 15:05:00]

Negative values are not allowed in block-steps (to avoid infinite loops)

>> range/step 1-6-2017 5-1-2017 [month + -1]
*** User Error: "Negative values not allowed in block step!"

Other types of step can be used in appropriate cases

>> range/step 10x81 1x0 1x9
== [10x81 9x72 8x63 7x54 6x45 5x36 4x27 3x18 2x9 1x0]
>> range/step 1.1.1 5.10.15 1.2.3
== [1.1.1 2.3.4 3.5.7 4.7.10 5.9.13]
>> range/step 5.10.15 1.1.1 1.2.3
== [5.10.15 4.8.12 3.6.9 2.4.6 1.2.3]

Negative steps can't be used in some cases, but instead reversing the result may be used

>> reverse range/step 5.10.15 1.1.1 1.2.3
== [1.2.3 2.4.6 3.6.9 4.8.12 5.10.15]

Limits

With /limit val2 is interpreted as number of values in range.
Default step is 1 up.

>> range/limit 5 10
== [5 6 7 8 9 10 11 12 13 14]
>> range/limit 5% 5
== [5% 6% 7% 8% 9%]
>> range/limit 1.2.3 5
== [1.2.3 2.3.4 3.4.5 4.5.6 5.6.7]

/step will add gaps and determine direction

>> range/limit/step 5 10 5
== [5 10 15 20 25 30 35 40 45 50]
>> range/limit/step 1-1-2017 5 [month - 1]
== [1-Jan-2017 1-Dec-2016 1-Nov-2016 1-Oct-2016 1-Sep-2016]
>> range/limit/step 1-1-2017 5 -24:0:0
== [1-Jan-2017/0:00:00 31-Dec-2016/0:00:00 30-Dec-2016/0:00:00 29-Dec-2016/0:00:00 28-Dec-2016/0:00:00]
>> range/limit/step 1-1-2017 5 10 * 24:0:0
== [1-Jan-2017 11-Jan-2017/0:00:00 21-Jan-2017/0:00:00 31-Jan-2017/0:00:00 10-Feb-2017/0:00:00]

Binary values

>> range #{F9} #{FF}
== [#{000000F9} #{000000FA} #{000000FB} #{000000FC} #{000000FD} #{000000FE} #{000000FF}]
>> range 2#{00000001} 2#{00000011}
== [#{00000001} #{00000002} #{00000003}]
>> range 64#{AQ==} 64#{BQ==}
== [#{00000001} #{00000002} #{00000003} #{00000004} #{00000005}]
>> range/enbase 64#{AQ==} 64#{BQ==} 2
== ["00000000000000000000000000000001" "00000000000000000000000000000010" "00000000000000000000000000000011" "00000000000000000000000000000100"...
>> range/enbase 64#{AQ==} 64#{BQ==} 64
== ["AAAAAQ==" "AAAAAg==" "AAAAAw==" "AAAABA==" "AAAABQ=="]
>> range/enbase/limit 64#{AQ==} 3 64
== ["AAAAAQ==" "AAAAAg==" "AAAAAw=="]
>> range/limit 64#{AQ==} 3
== [#{00000001} #{00000002} #{00000003}]
>> range/limit/step 64#{AQ==} 5 2
== [#{00000001} #{00000003} #{00000005} #{00000007} #{00000009}]

@hiiamboris
Copy link

Pair ranges are only useful as 2D ranges. E.g. range 2x2 4x4 should produce [2x2 3x2 4x2 2x3 3x3 4x3 2x4 3x4 4x4]

@toomasv
Copy link
Author

toomasv commented Jun 19, 2021

Probably you are right.

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