RosettaCodeData/Task/Range-expansion/8th/range-expansion.8th
2016-12-05 23:44:36 +01:00

61 lines
1.6 KiB
Text

\ Given a low and high limit, create an array containing the numbers in the
\ range, inclusive:
: n:gen-range \ low hi -- a
\ make sure they are in order:
2dup n:> if swap then
\ fill the array with the values:
[] ' a:push
2swap loop ;
\ Take a string, either "X" or "X-Y", and correctly return either a number (if
\ "X") or an array of numbers (if "X-Y"):
: n:expand-one \ s -- n | a[n,..m]
\ First see if we can parse a number. This works in the "X" case:
dup >n null? if
\ Failed >n because it's (possibly) "X-Y"
drop
\ not a valid number, might be a range
\ We'll use a capturing regex to handle the different cases correctly:
/(-?[0-9]+)-(-?[0-9]+)/ tuck r:match
\ If the regex matches three (the whole string, plus the two captured
\ expressions) then it's a valid "X-Y":
3 n:= if
1 r:@ >n swap 2 r:@ >n nip
\ generate the range:
n:gen-range
else
\ The regex didn't match, so we got garbage. Therefore, return a 'null':
drop null
then
else
\ It was a "X", just drop the original string:
nip
then
;
\ Take an array (possibly) containing other arrays, and flatten any contained
\ arrays so the result is a simple array:
: a:flatten \ a1 -- a2
[] >r
(
nip
array? if
a:flatten r> swap a:+ >r
else
r> swap a:push >r
then
) a:each drop r> ;
\ Take a comma-delimited string of ranges, and expand it into an array of
\ numbers:
: n:range-expand \ str -- a
"," s:/
' n:expand-one a:map
a:flatten ;
\ Process a list:
"-6,-3--1,3-5,7-11,14,15,17-20"
n:range-expand
\ print the expanded list:
. cr bye