Range expansion
You are encouraged to solve this task according to the task description, using any language you may know.
A format for expressing an ordered list of integers is to use a comma separated list of either
- individual integers
- Or a range of integers denoted by the starting integer separated from the end integer in the range by a dash, '-'. (The range includes all integers in the interval including both endpoints)
- The range syntax is to be used only for, and for every range that expands to more than two values.
Example
The list of integers:
- -6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20
Is accurately expressed by the range expression:
- -6,-3-1,3-5,7-11,14,15,17-20
(And vice-versa).
The task
Expand the range description:
- -6,-3-1,3-5,7-11,14,15,17-20
C.f. Range extraction
ALGOL 68
<lang algol68>MODE YIELDINT = PROC(INT)VOID;
MODE RANGE = STRUCT(INT lwb, upb); MODE RANGEINT = UNION(RANGE, INT);
OP SIZEOF = ([]RANGEINT list)INT: (
- determine the length of the output array #
INT upb := LWB list - 1; FOR key FROM LWB list TO UPB list DO CASE list[key] IN (RANGE value): upb +:= upb OF value - lwb OF value + 1, (INT): upb +:= 1 ESAC OD; upb
);
PROC gen range expand = ([]RANGEINT list, YIELDINT yield)VOID:
FOR key FROM LWB list TO UPB list DO CASE list[key] IN (RANGE range): FOR value FROM lwb OF range TO upb OF range DO yield(value) OD, (INT int): yield(int) ESAC OD;
PROC range expand = ([]RANGEINT list)[]INT: (
[LWB list: LWB list + SIZEOF list - 1]INT out; INT upb := LWB out - 1;
- FOR INT value IN # gen range expand(list, # ) DO #
- (INT value)VOID:
out[upb +:= 1] := value
- OD #);
out
);
test:(
[]RANGEINT list = (-6, RANGE(-3, 1), RANGE(3, 5), RANGE(7, 11), 14, 15, RANGE(17, 20)); print((range expand(list), new line))
)</lang> Output:
-6 -3 -2 -1 +0 +1 +3 +4 +5 +7 +8 +9 +10 +11 +14 +15 +17 +18 +19 +20
J
<lang j>require'strings' to=: (+ i.)/@:(0 1 + -~/\) num=: _&". normaliz=: rplc&(',-';',_';'--';'-_')@,~&',' lumps=:<@(num`([:to num;._1@,~&'-')@.('-'&e.));._1 rngexp=: ;@lumps@normaliz</lang>
Example: <lang j> rngexp '-6,-3-1,3-5,7-11,14,15,17-20' _6 _3 _2 _1 0 1 3 4 5 7 8 9 10 11 14 15 17 18 19 20
rngexp '-6,-3--1,3-5,7-11,14,15,17-20'
_6 _3 _2 _1 3 4 5 7 8 9 10 11 14 15 17 18 19 20</lang>
MUMPS
<lang MUMPS> RANGEXP(X) ;Integer range expansion
NEW Y,I,J,X1,H SET Y="" FOR I=1:1:$LENGTH(X,",") DO .S X1=$PIECE(X,",",I) FOR Q:$EXTRACT(X1)'=" " S X1=$EXTRACT(X1,2,$LENGTH(X1)) ;clean up leading spaces .SET H=$FIND(X1,"-")-1 .IF H=1 SET H=$FIND(X1,"-",(H+1))-1 ;If the first value is negative ignore that "-" .IF H<0 SET Y=$SELECT($LENGTH(Y)=0:Y_X1,1:Y_","_X1) .IF '(H<0) FOR J=+$EXTRACT(X1,1,(H-1)):1:+$EXTRACT(X1,(H+1),$LENGTH(X1)) SET Y=$SELECT($LENGTH(Y)=0:J,1:Y_","_J) KILL I,J,X1,H QUIT Y</lang>
Example:
USER>SET T="-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20 " USER>WRITE $$RANGEXP^ROSETTA(T) -6,-3,-2,-1,0,1,3,4,5,7,8,9,10,11,14,15,17,18,19,20 USER>SET U="-10--8,"_T WRITE U -10--8,-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20 USER>WRITE $$RANGEXP^ROSETTA(U) -10,-9,-8,-6,-3,-2,-1,0,1,3,4,5,7,8,9,10,11,14,15,17,18,19,20
OCaml
<lang ocaml>#load "str.cma"
let range a b =
if b < a then invalid_arg "range"; let rec aux i acc = if i = b then List.rev(i::acc) else aux (succ i) (i::acc) in aux a []
let parse_piece s =
try Scanf.sscanf s "%d-%d" (fun a b -> range a b) with _ -> [int_of_string s]
let range_expand rng =
let ps = Str.split (Str.regexp_string ",") rng in List.flatten (List.map parse_piece ps)
let () =
let rng = "-6,-3-1,3-5,7-11,14,15,17-20" in let exp = range_expand rng in List.iter (Printf.printf " %d") exp; print_newline()</lang>
Oz
<lang oz>declare
fun {Expand RangeDesc} {Flatten {Map {ParseDesc RangeDesc} ExpandRange}} end
fun {ParseDesc Txt} {Map {String.tokens Txt &,} ParseRange} end
fun {ParseRange R} if {Member &- R.2} then First Second in {String.token R.2 &- ?First ?Second} {String.toInt R.1|First}#{String.toInt Second} else Singleton = {String.toInt R} in Singleton#Singleton end end
fun {ExpandRange From#To} {List.number From To 1} end
in
{Inspect {Expand "-6,-3-1,3-5,7-11,14,15,17-20"}}</lang>
Python
<lang python>def rangeexpand(txt):
lst = [] for r in txt.split(','): if '-' in r[1:]: r0, r1 = r[1:].split('-', 1) lst += range(int(r[0] + r0), int(r1) + 1) else: lst.append(int(r)) return lst
print(rangeexpand('-6,-3-1,3-5,7-11,14,15,17-20'))</lang>
Another variant, using regular expressions to parse the ranges:
<lang python>import re
def rangeexpand(txt):
lst = [] for rng in txt.split(','): start,end = re.match('^(-?\d+)(?:-(-?\d+))?$', rng).groups() if end: lst.extend(xrange(int(start),int(end)+1)) else: lst.append(int(start)) return lst</lang>
Sample output
[-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]
Ruby
<lang ruby>def range_expand(rng)
lst = rng.split(',').collect do |part| begin Integer(part) rescue ArgumentError if part.scan(/^(-?\d+)-(-?\d+)$/) ($1.to_i .. $2.to_i).to_a else raise ArgumentError, "Can't parse part of range: '#{part}'" end end end lst.flatten
end
p range_expand('-6,-3-1,3-5,7-11,14,15,17-20')</lang>
output:
[-6, -3, -2, -1, 0, 1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]
Tcl
<lang tcl>proc rangeExpand desc {
set result {} foreach term [split $desc ","] {
set count [scan $term %d-%d from to] if {$count == 1} { lappend result $from } elseif {$count == 2} { for {set i $from} {$i <= $to} {incr i} {lappend result $i} }
} return $result
}
puts [rangeExpand "-6,-3-1,3-5,7-11,14,15,17-20"]</lang> Output:
-6 -3 -2 -1 0 1 3 4 5 7 8 9 10 11 14 15 17 18 19 20