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
Note that the second element above, is the range from minus 3 to minus 1.
C.f. Range extraction
Ada
The function Expand takes a string and returns a corresponding array of integers. Upon syntax errors Constraint_Error is propagated: <lang Ada>with Ada.Text_IO; use Ada.Text_IO; procedure Test_Range_Expansion is
type Sequence is array (Positive range <>) of Integer; function Expand (Text : String) return Sequence is To : Integer := Text'First; Count : Natural := 0; Low : Integer; function Get return Integer is From : Integer := To; begin if Text (To) = '-' then To := To + 1; end if; while To <= Text'Last loop case Text (To) is when ',' | '-' => exit; when others => To := To + 1; end case; end loop; return Integer'Value (Text (From..To - 1)); end Get; begin while To <= Text'Last loop -- Counting items of the list Low := Get; if To > Text'Last or else Text (To) = ',' then Count := Count + 1; else To := To + 1; Count := Count + Get - Low + 1; end if; To := To + 1; end loop; return Result : Sequence (1..Count) do Count := 0; To := Text'First; while To <= Text'Last loop -- Filling the list Low := Get; if To > Text'Last or else Text (To) = ',' then Count := Count + 1; Result (Count) := Low; else To := To + 1; for Item in Low..Get loop Count := Count + 1; Result (Count) := Item; end loop; end if; To := To + 1; end loop; end return; end Expand; procedure Put (S : Sequence) is First : Boolean := True; begin for I in S'Range loop if First then First := False; else Put (','); end if; Put (Integer'Image (S (I))); end loop; end Put;
begin
Put (Expand ("-6,-3--1,3-5,7-11,14,15,17-20"));
end Test_Range_Expansion;</lang>
- Output:
-6,-3,-2,-1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20
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 +3 +4 +5 +7 +8 +9 +10 +11 +14 +15 +17 +18 +19 +20
AutoHotkey
<lang AutoHotkey>msgbox % expand("-6,-3--1,3-5,7-11,14,15,17-20")
expand( range ) {
p := 0 while p := RegExMatch(range, "\s*(-?\d++)(?:\s*-\s*(-?\d++))?", f, p+1+StrLen(f)) loop % (f2 ? f2-f1 : 0) + 1 ret .= "," (A_Index-1) + f1 return SubStr(ret, 2)
}</lang>
Bracmat
<lang bracmat> ( expandRanges
= a b L . @( !arg : (#(?a:?b)|#?a "-" #?b) (:?L|"," [%(expandRanges$!sjt:?L)) ) & whl ' ( (!L:&!b|(!b,!L)) : ?L & -1+!b:~<!a:?b ) & !L | )
& out$(str$(expandRanges$"-6,-3--1,3-5,7-11,14,15,17-20")) </lang> Output:
-6,-3,-2,-1,3,4,5,7,8,9,10,11,14,15,17,18,19,20
C
Recursive descent parser. <lang c>#include <stdio.h>
- include <stdlib.h>
- include <ctype.h>
/* BNFesque rangelist := (range | number) [',' rangelist] range := number '-' number */
int get_list(char *, char **); int get_rnge(char *, char **);
/* parser only parses; what to do with parsed items is up to
- the add_number and and_range functions */
void add_number(int x); int add_range(int x, int y);
- define skip_space while(isspace(*s)) s++
- define get_number(x, s, e) (x = strtol(s, e, 10), *e != s)
int get_list(char *s, char **e) { int x; while (1) { skip_space; if (!get_rnge(s, e) && !get_number(x, s, e)) break; s = *e;
skip_space; if ((*s) == '\0') { putchar('\n'); return 1; } if ((*s) == ',') { s++; continue; } break; } *e = s; printf("\nSyntax error at %s\n", s); return 0; }
int get_rnge(char *s, char **e) { int x, y; char *ee; if (!get_number(x, s, &ee)) return 0; s = ee;
skip_space; if (*s != '-') { *e = s; return 0; } s++; if(!get_number(y, s, e)) return 0; return add_range(x, y); }
void add_number(int x) { printf("%d ", x); }
int add_range(int x, int y) { if (y <= x) return 0; while (x <= y) printf("%d ", x++); return 1; }
int main() { char *end;
/* this is correct */ if (get_list("-6,-3--1,3-5,7-11,14,15,17-20", &end)) puts("Ok");
/* this is not. note the subtle error: "-6 -3" is parsed * as range(-6, 3), so synax error comes after that */ get_list("-6 -3--1,3-5,7-11,14,15,17-20", &end);
return 0; }</lang>
- Output:
-6 -3 -2 -1 3 4 5 7 8 9 10 11 14 15 17 18 19 20 Ok -6 -5 -4 -3 -2 -1 0 1 2 3 Syntax error at --1,3-5,7-11,14,15,17-20
C#
<lang csharp>using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions;
class Program {
static void Main(string[] args) { var rangeString = "-6,-3--1,3-5,7-11,14,15,17-20"; var matches = Regex.Matches(rangeString, @"((?<f>-?\d+)-(?-?\d+))|(-?\d+)");
var values = new List<string>(); foreach (var m in matches.OfType<Match>()) { if (m.Length == 2) { values.Add(m.Value); continue; }
var start = Convert.ToInt32(m.Groups["f"].Value); var end = Convert.ToInt32(m.Groups["s"].Value) + 1;
values.AddRange(Enumerable.Range(start, end - start).Select(v => v.ToString())); }
Console.WriteLine(string.Join(", ", values)); }
}</lang>
C++
<lang cpp>#include <iostream>
- include <sstream>
- include <iterator>
- include <climits>
- include <deque>
// parse a list of numbers with ranges // // arguments: // is: the stream to parse // out: the output iterator the parsed list is written to. // // returns true if the parse was successful. false otherwise template<typename OutIter>
bool parse_number_list_with_ranges(std::istream& is, OutIter out)
{
int number; // the list always has to start with a number while (is >> number) { *out++ = number;
char c; if (is >> c) switch(c) { case ',': continue; case '-': { int number2; if (is >> number2) { if (number2 < number) return false; while (number < number2) *out++ = ++number; char c2; if (is >> c2) if (c2 == ',') continue; else return false; else return is.eof(); } else return false; } default: return is.eof(); } else return is.eof(); } // if we get here, something went wrong (otherwise we would have // returned from inside the loop) return false;
}
int main() {
std::istringstream example("-6,-3--1,3-5,7-11,14,15,17-20"); std::deque<int> v; bool success = parse_number_list_with_ranges(example, std::back_inserter(v)); if (success) { std::copy(v.begin(), v.end()-1, std::ostream_iterator<int>(std::cout, ",")); std::cout << v.back() << "\n"; } else std::cout << "an error occured.";
}</lang>
- Output:
-6,-3,-2,-1,3,4,5,7,8,9,10,11,14,15,17,18,19,20
Clojure
There is a split method in clojure.contrib, but I don't know if it is able to skip first character to so that (split "-8--8") => (-8 -8)
.
<lang clojure>(defn split [s sep]
(defn skipFirst x & xs :as s
(cond (empty? s) [nil nil] (= x sep) [x xs] true [nil s]))
(loop [lst '(), s s]
(if (empty? s) (reverse lst) (let [[hd trunc] (skipFirst s) [word news] (split-with #(not= % sep) trunc) cWord (cons hd word)] (recur (cons (apply str cWord) lst) (apply str (rest news)))))))
(defn parseRange x & xs :as s
(if (some #(= % \-) xs)
(let [[r0 r1] (split s \-)] (range (read-string r0) (inc (read-string r1)))) (list (read-string (str s))))))
(defn rangeexpand [s]
(flatten (map parseRange (split s \,))))
> (rangeexpand "-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>
Common Lisp
<lang lisp>(defun expand-ranges (string)
(loop with prevnum = nil for idx = 0 then (1+ nextidx) for (number nextidx) = (multiple-value-list (parse-integer string :start idx :junk-allowed t)) append (cond (prevnum (prog1 (loop for i from prevnum to number collect i) (setf prevnum nil))) ((and (< nextidx (length string)) (char= (aref string nextidx) #\-)) (setf prevnum number) nil) (t (list number))) while (< nextidx (length string))))
CL-USER> (expand-ranges "-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>
D
<lang d>import std.stdio, std.regex, std.string, std.conv, std.range;
int[] rangeExpand(in string txt) /*pure nothrow*/ {
typeof(return) result;
foreach (r; std.string.split(txt, ",")) { const m = r.match(r"^(-?\d+)(-?(-?\d+))?$").captures.array(); result ~= m[2].empty ? [to!int(m[1])] : iota(to!int(m[1]), to!int(m[3])+1).array(); } return result;
}
void main() {
rangeExpand("-6,-3--1,3-5,7-11,14,15,17-20").writeln();
}</lang>
- Output:
[-6, -3, -2, -1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]
F#
<lang fsharp>open System.Text.RegularExpressions
// simplify regex matching with an active pattern let (|Regexp|_|) pattern txt =
match Regex.Match(txt, pattern) with | m when m.Success -> [for g in m.Groups -> g.Value] |> List.tail |> Some | _ -> None
// Parse and expand a single range description. // string -> int list let parseRange r =
match r with | Regexp @"^(-?\d+)-(-?\d+)$" [first; last] -> [int first..int last] | Regexp @"^(-?\d+)$" [single] -> [int single] | _ -> failwithf "illegal range format: %s" r
let expand (desc:string) =
desc.Split(',') |> List.ofArray |> List.collect parseRange
printfn "%A" (expand "-6,-3--1,3-5,7-11,14,15,17-20")</lang>
- Output:
[-6; -3; -2; -1; 3; 4; 5; 7; 8; 9; 10; 11; 14; 15; 17; 18; 19; 20]
Go
A version rather strict with input <lang go>package main
import (
"fmt" "strconv" "strings"
)
const input = "-6,-3--1,3-5,7-11,14,15,17-20"
func main() {
fmt.Println("range:", input) var r []int var last int for _, part := range strings.Split(input, ",") { if i := strings.Index(part[1:], "-"); i == -1 { n, err := strconv.Atoi(part) if err != nil { fmt.Println(err) return } if len(r) > 0 { if last == n { fmt.Println("duplicate value:", n) return } else if last > n { fmt.Println("values not ordered:", last, ">", n) return } } r = append(r, n) last = n } else { n1, err := strconv.Atoi(part[:i+1]) if err != nil { fmt.Println(err) return } n2, err := strconv.Atoi(part[i+2:]) if err != nil { fmt.Println(err) return } if n2 < n1+2 { fmt.Println("invalid range:", part) return } if len(r) > 0 { if last == n1 { fmt.Println("duplicate value:", n1) return } else if last > n1 { fmt.Println("values not ordered:", last, ">", n1) return } } for i = n1; i <= n2; i++ { r = append(r, i) } last = n2 } } fmt.Println("expanded:", r)
}</lang>
Groovy
Ad Hoc Solution:
- translate the task's range syntax into Groovy range syntax
- wrap with list delimiters
- evaluate the script expression
- flatten the nested lists
- express as a string
- unwrap the list delimiters
<lang groovy>def expandRanges = { compressed ->
Eval.me('['+compressed.replaceAll(~/(\d)-/, '$1..')+']').flatten().toString()[1..-2]
}</lang> Test: <lang groovy>def s = '-6,-3--1,3-5,7-11,14,15,17-20' println (expandRanges(s))</lang>
- Output:
-6, -3, -2, -1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20
Haskell
Given either of the below implementations of expandRange
:
<lang haskell>> expandRange "-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>
With conventional list processing
<lang haskell>expandRange :: String -> [Int] expandRange = concatMap f . split ','
where f str@(c : cs) | '-' `elem` cs = [read (c : a) .. read b] | otherwise = [read str] where (a, _ : b) = break (== '-') cs
split :: Eq a => a -> [a] -> a split delim [] = [] split delim l = a : split delim (dropWhile (== delim) b)
where (a, b) = break (== delim) l</lang>
With a parser
<lang haskell>import Control.Monad import Text.ParserCombinators.Parsec
expandRange :: String -> [Int] expandRange s = case parse rangeParser "" s of Right l -> l
rangeParser :: Parser [Int] rangeParser = liftM concat $ item `sepBy` char ','
where item = do n1 <- num n2 <- option n1 $ char '-' >> num return [n1 .. n2] num :: Parser Int num = liftM read $ liftM2 (++) (option "" $ string "-") (many1 digit)</lang>
Icon and Unicon
<lang Icon>procedure main() s := "-6,-3--1,3-5,7-11,14,15,17-20" write("Input string := ",s) write("Expanded list := ", list2string(range_expand(s)) | "FAILED") end
procedure range_expand(s) #: return list of integers extracted from an ordered string representation local R,low,high R := []
s ? until pos(0) do {
put(R,low := integer(tab(upto(',-')|0))| fail) # get lower bound if ="-" || (high := integer(tab(find(",")|0))|fail) then until low = high do put(R,low +:= 1) # find range ="," }
return R end
procedure list2string(L) #: helper function to convert a list to a string local s
every (s := "[ ") ||:= !L || " " return s || "]"
end</lang>
- Output:
Input string := -6,-3--1,3-5,7-11,14,15,17-20 Expanded list := [ -6 -3 -2 -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 3 4 5 7 8 9 10 11 14 15 17 18 19 20</lang>
Java
<lang java>import java.util.*; import java.util.regex.*;
class Range implements Enumeration {
private int clower, cupper; private int value; private boolean inrange; private Scanner ps = null; private String ss;
private static String del = "\\s*,\\s*";
public Range(String s) { ss = s; reset(); }
public boolean hasMoreElements() { return (inrange && (value >= clower && value <= cupper)) || ps.hasNext(); }
public Object nextElement() throws NoSuchElementException { if (!hasMoreElements()) throw new NoSuchElementException(); if (inrange && (value >= clower && value <= cupper)) { value++; return value-1; } inrange = false; String n = ps.next(); if (n.matches("[+-]?\\d+-[+-]?\\d+")) { Scanner ls = new Scanner(n); ls.findInLine("([+-]?\\d+)-([+-]?\\d+)"); MatchResult r = ls.match(); clower = Integer.parseInt(r.group(1)); cupper = Integer.parseInt(r.group(2)); value = clower+1; inrange = true; ls.close(); return clower; } return Integer.parseInt(n); } public void reset() { if (ps != null) ps.close(); ps = new Scanner(ss).useDelimiter(del); inrange = false; }
protected void finalize() throws Throwable { ps.close(); super.finalize(); }
}
class rangexp {
public static void main(String[] args) { Range r = new Range("-6,-3--1,3-5,7-11,14,15,17-20"); while (r.hasMoreElements()) { System.out.print(r.nextElement() + " "); } System.out.println(); }
}</lang>
K
<lang k>grp : {1_'(&x=*x)_ x:",",x} pos : {:[3=l:#p:&"-"=x;0,p@1;2=l;p;0=*p;,0;0,p]} conv: 0${(x;1_ y)}/'{(pos x)_ x}' expd: {,/@[x;&2=#:'x;{(*x)+!1+,/-':x}]} rnge: {expd@conv grp x}</lang>
- Example:
<lang k> rnge "-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>
Liberty BASIC
<lang lb>print ExpandRange$( "-6,-3--1,3-5,7-11,14,15,17-20") end
function ExpandRange$( compressed$)
for i = 1 to ItemCount( compressed$, ",") item$ = word$( compressed$, i, ",") dash = instr( item$, "-", 2) 'dash that is not the first character, is a separator if dash then for k = val( left$( item$, dash - 1)) to val( mid$( item$, dash + 1)) ExpandRange$ = ExpandRange$ + str$( k) + "," next k else ExpandRange$ = ExpandRange$ + item$ + "," end if next i ExpandRange$ = left$( ExpandRange$, len( ExpandRange$) - 1)
end function
function ItemCount( list$, separator$)
while word$(list$, ItemCount + 1, separator$) <> "" ItemCount = ItemCount + 1 wend
end function</lang>
- Output:
-6,-3,-2,-1,3,4,5,7,8,9,10,11,14,15,17,18,19,20
Mathematica
<lang Mathematica>rangeexpand[ rng_ ] := Module[ { step1 }, step1 = StringSplit[StringReplacePart[rng,"S",StringPosition[ rng,DigitCharacter~~"-"] /. {x_,y_} -> {y,y}],","]; Flatten@ToExpression/@Quiet@StringReplace[step1,x__~~"S"~~y__->"Range["<>x<>","<>y<>"]"] ]</lang>
- Example:
rangeexpand["-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}
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 U="-6,-3--1,3-5,7-11,14,15,17-20" USER>WRITE $$RANGEXP^ROSETTA(U) -6,-3,-2,-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
{System.showInfo {Value.toVirtualString {Expand "-6,-3--1,3-5,7-11,14,15,17-20"} 100 100}}</lang>
- Sample output:
<lang oz>[~6 ~3 ~2 ~1 3 4 5 7 8 9 10 11 14 15 17 18 19 20]</lang>
Perl
One-liner: <lang Perl>sub rangex {
map { /^(.*\d)-(.+)$/ ? $1..$2 : $_ } split /,/, shift
}
- Test and display
print join(',', rangex('-6,-3--1,3-5,7-11,14,15,17-20')), "\n";</lang>
- Output:
-6,-3,-2,-1,3,4,5,7,8,9,10,11,14,15,17,18,19,20
Alternative: <lang Perl>sub rangex {
(my $range = shift) =~ s/(?<=\d)-/../g; eval $range;
}</lang>
Perl 6
<lang Perl6>sub range-expansion (Str $range-description) {
my $range-pattern = rx/ ( '-'? \d+ ) '-' ( '-'? \d+) /; my &expand = -> $term { $term ~~ $range-pattern ?? +$0..+$1 !! $term }; return $range-description.split(',').map(&expand)
}
say range-expansion('-6,-3--1,3-5,7-11,14,15,17-20').join(', ');</lang>
- Output:
-6, -3, -2, -1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20
PHP
<lang PHP>function rangex($str) {
$lst = array(); foreach (explode(',', $str) as $e) { if (strpos($e, '-', 1) !== FALSE) { list($a, $b) = explode('-', substr($e, 1), 2); $lst = array_merge($lst, range($e[0] . $a, $b)); } else { $lst[] = (int) $e; } } return $lst;
}</lang>
PicoLisp
<lang PicoLisp>(de rangeexpand (Str)
(make (for S (split (chop Str) ",") (if (index "-" (cdr S)) (chain (range (format (head @ S)) (format (tail (- -1 @) S)) ) ) (link (format S)) ) ) ) )</lang>
- Output:
: (rangeexpand "-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)
PL/I
<lang PL/I>range_expansion:
procedure options (main);
get_number:
procedure (Number, c, eof); declare number fixed binary (31), c character (1), eof bit (1) aligned; declare neg fixed binary (1);
number = 0; eof = false; do until (c ^= ' '); get edit (c) (a(1)); end; if c = '-' then do; get edit (c) (a(1)); neg = -1; end; else neg = 1; do forever; select (c); when ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') number = number*10 + c; when (',', '-') do; number = neg*number; return; end; otherwise signal error; end; on endfile (sysin) go to exit; get edit (c) (a(1)); end;
exit:
number = neg*number; eof = true;
end get_Number;
declare c character, (i, range_start, range_end) fixed binary (31); declare eof bit (1) aligned; declare true bit (1) value ('1'b), false bit (1) value ('0'b); declare delimiter character (1) initial (' '); declare out file output;
open file (out) output title ('/out, type(text),recsize(80)'); do while (^eof); call get_number(range_start, c, eof); if c = '-' then /* we have a range */ do; call get_number (range_end, c, eof); do i = range_start to range_end; put file (out) edit (delimiter, i) (a, f(3)); end; end; else do; put file (out) edit (delimiter, range_start) (a, f(3)); end; delimiter = ','; end;
end range_expansion;</lang>
- Output:
-6, -3, -2, -1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20
Prolog
The code uses three predicates extract_Range/2, study_Range/2 and pack_Range/2.
Every predicate works in both directions arg1 towards arg2 and arg2 towards arg1, so that Range expansion and Range extraction work with the same predicates but in reverse order.
<lang Prolog>range_expand :-
L = '-6,-3--1,3-5,7-11,14,15,17-20',
writeln(L),
atom_chars(L, LA),
extract_Range(LA, R),
maplist(study_Range, R, LR),
pack_Range(LX, LR),
writeln(LX).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % extract_Range(?In, ?Out) % In : '-6,-3--1,3-5,7-11,14,15,17-20' % Out : [-6], [-3--1], [3-5],[7-11], [14],[15], [17-20] % extract_Range([], []).
extract_Range(X , [Range | Y1]) :- get_Range(X, U-U, Range, X1), extract_Range(X1, Y1).
get_Range([], Range-[], Range, []). get_Range([','|B], Range-[], Range, B) :- !.
get_Range([A | B], EC, Range, R) :- append_dl(EC, [A | U]-U, NEC), get_Range(B, NEC, Range, R).
append_dl(X-Y, Y-Z, X-Z).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % study Range(?In, ?Out) % In : [-6] % Out : [-6,-6] % % In : [-3--1] % Out : [-3, -1] % study_Range(Range1, [Deb, Deb]) :-
catch(number_chars(Deb, Range1), Deb, false).
study_Range(Range1, [Deb, Fin]) :-
append(A, ['-'|B], Range1), A \= [], number_chars(Deb, A), number_chars(Fin, B).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %
- - use_module(library(clpfd)).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Pack Range(?In, ?Out) % In : -6, % Out : [-6] % % In : -3, -2,-1 % Out : [-3,-1] % pack_Range([],[]).
pack_Range([X|Rest],[[X | V]|Packed]):-
run(X,Rest, [X|V], RRest), pack_Range(RRest,Packed).
run(Fin,[Other|RRest], [Deb, Fin],[Other|RRest]):-
Fin #\= Deb,
Fin #\= Deb + 1,
Other #\= Fin+1.
run(Fin,[],[_Var, Fin],[]).
run(Var,[Var1|LRest],[Deb, Fin], RRest):- Fin #\= Deb, Fin #\= Deb + 1, Var1 #= Var + 1, run(Var1,LRest,[Deb, Fin], RRest).
run(Val,[Other|RRest], [Val, Val],[Other|RRest]).</lang>
- Output:
?- range_expand. -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] true
PureBasic
<lang PureBasic>Procedure rangeexpand(txt.s, List outputList())
Protected rangesCount = CountString(txt, ",") + 1 Protected subTxt.s, r, rangeMarker, rangeStart, rangeFinish, rangeIncrement, i LastElement(outputList()) For r = 1 To rangesCount subTxt = StringField(txt, r, ",") rangeMarker = FindString(subTxt, "-", 2) If rangeMarker rangeStart = Val(Mid(subTxt, 1, rangeMarker - 1)) rangeFinish = Val(Mid(subTxt, rangeMarker + 1)) If rangeStart > rangeFinish rangeIncrement = -1 Else rangeIncrement = 1 EndIf i = rangeStart - rangeIncrement Repeat i + rangeIncrement AddElement(outputList()): outputList() = i Until i = rangeFinish Else AddElement(outputList()): outputList() = Val(subTxt) EndIf Next
EndProcedure
Procedure outputListValues(List values())
Print("[ ") ForEach values() Print(Str(values()) + " ") Next PrintN("]")
EndProcedure
If OpenConsole()
NewList values() rangeexpand("-6,-3--1,3-5,7-11,14,15,17-20", values()) outputListValues(values()) Print(#CRLF$ + #CRLF$ + "Press ENTER to exit") Input() CloseConsole()
EndIf</lang>
- Output:
[ -6 -3 -2 -1 3 4 5 7 8 9 10 11 14 15 17 18 19 20 ]
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>
- Output:
[-6, -3, -2, -1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]
REXX
<lang rexx>/*REXX program to expand a range of integers into a list. */
old='-6,-3--1,3-5,7-11,14,15,17-20' /*original list of nums/ranges. */
say 'old list='old /*show old list of nums/ranges. */ a=translate(old,,',') /*translate commas to blanks */ new= /*new list of numbers (so far). */
do j=1 /*process each number or range. */ y= /*nullify the output number(s). */ parse var a x a; if x== then leave /*get next num/range. Null? Done.*/ minus=pos('-',x,2) /*find location of a dash (maybe)*/ if minus\==0 then do /*if found then process range. */ do k=left(x,minus-1) to substr(x,minus+1) y=y k /*build one integer at a time. */ end x= /*nullify X, it's still a range. */ end new=new x y /*append them to the new list. */ end
new=space(new) /*remove extraneous blanks. */ say 'new list='new /*show the new list of numbers. */</lang>
- Output:
old list=-6,-3--1,3-5,7-11,14,15,17-20 new list=-6 -3 -2 -1 3 4 5 7 8 9 10 11 14 15 17 18 19 20
Ruby
<lang ruby>def range_expand(rng)
rng.split(',').collect do |part| if part =~ /^(-?\d+)-(-?\d+)$/ ($1.to_i .. $2.to_i).to_a else Integer(part) end end.flatten
end
p range_expand('-6,-3--1,3-5,7-11,14,15,17-20')</lang>
- Output:
[-6, -3, -2, -1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20]
Scala
<lang ruby>def rangex(str: String): Seq[Int] =
str split "," flatMap { (s) => val r = """(-?\d+)(?:-(-?\d+))?""".r val r(a,b) = s if (b == null) Seq(a.toInt) else a.toInt to b.toInt }</lang>
Scheme
<lang scheme>(define split
(lambda (str char skip count) (let ((len (string-length str))) (let loop ((index skip) (last-index 0) (result '())) (if (= index len) (reverse (cons (substring str last-index) result)) (if (eq? char (string-ref str index)) (loop (if (= count (+ 2 (length result))) len (+ index 1)) (+ index 1) (cons char (cons (substring str last-index index) result))) (loop (+ index 1) last-index result)))))))
(define range-expand
(lambda (str) (for-each (lambda (token) (if (char? token) (display token) (let ((range (split token #\- 1 2))) (if (null? (cdr range)) (display (car range)) (do ((count (string->number (list-ref range 0)) (+ 1 count)) (high (string->number (list-ref range 2)))) ((= count high) (display high)) (display count) (display ",")))))) (split str #\, 0 0)) (newline)))</lang>
- Output:
(range-expand "-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
Seed7
The library scanstri.s7i defines the function getInteger to extract substrings with integer literals (optional sign followed by a sequence of digits) from a string. The integer literals are converted to the type integer with the parse operator. <lang seed7>$ include "seed7_05.s7i";
include "scanstri.s7i";
const func array integer: rangeExpansion (in var string: rangeStri) is func
result var array integer: numbers is 0 times 0; local var integer: number is 0; begin while rangeStri <> "" do number := integer parse getInteger(rangeStri); numbers &:= number; if startsWith(rangeStri, "-") then rangeStri := rangeStri[2 ..]; for number range succ(number) to integer parse getInteger(rangeStri) do numbers &:= number; end for; end if; if startsWith(rangeStri, ",") then rangeStri := rangeStri[2 ..]; elsif rangeStri <> "" then raise RANGE_ERROR; end if; end while; end func;
const proc: main is func
local var integer: number is 0; begin for number range rangeExpansion("-6,-3--1,3-5,7-11,14,15,17-20") do write(number <& " "); end for; writeln; end func;</lang>
- Output:
-6 -3 -2 -1 3 4 5 7 8 9 10 11 14 15 17 18 19 20
SNOBOL4
<lang SNOBOL4>* # Return range n1 .. n2
define('range(n1,n2)') :(range_end)
range range = range n1 ','; n1 = lt(n1,n2) n1 + 1 :s(range)
range rtab(1) . range :(return)
range_end
define('rangex(range)d1,d2') num = ('-' | ) span('0123456789') :(rangex_end)
rangex range num . d1 '-' num . d2 = range(d1,d2) :s(rangex)
rangex = range :(return)
rangex_end
- # Test and display
output = rangex('-6,-3--1,3-5,7-11,14,15,17-20')
end</lang>
- Output:
-6,-3,-2,-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 3 4 5 7 8 9 10 11 14 15 17 18 19 20
TUSCRIPT
<lang tuscript>$$ MODE TUSCRIPT rangednrs="-6,-3--1,3-5,7-11,14,15,17-20" expandnrs=SPLIT (rangednrs,":,:")
LOOP/CLEAR r=expandnrs
test=STRINGS (r,":><-><<>>/:") sz_test=SIZE (test) IF (sz_test==1) THEN expandnrs=APPEND (expandnrs,r) ELSE r=SPLIT (r,"::<|->/::-:",beg,end) expandnrs=APPEND (expandnrs,beg) LOOP/CLEAR next=beg,end next=next+1 expandnrs=APPEND (expandnrs,next) IF (next==end) EXIT ENDLOOP ENDIF
ENDLOOP expandnrs= JOIN (expandnrs,",")
PRINT expandnrs</lang>
- Output:
-6,-3,-2,-1,3,4,5,7,8,9,10,11,14,15,17,18,19,20
Ursala
<lang Ursala>#import std
- import int
rex = sep`,; zrange+*= %zp~~htttPzztPQhQXbiNC+ rlc ~&r~=`-
- cast %zL
t = rex '-6,-3--1,3-5,7-11,14,15,17-20'</lang>
- Output:
<-6,-3,-2,-1,3,4,5,7,8,9,10,11,14,15,17,18,19,20>
VBA
<lang VBA>Public Function RangeExpand(AString as string) ' return a list with the numbers expressed in AString Dim Splits() As String Dim List() As Integer Dim count As Integer
count = -1 'to start a zero-based List() array ' first split it using comma as delimiter Splits = Split(AString, ",") ' process all fragments For Each fragment In Splits
'is there a "-" in it (do not consider first character)? P = InStr(2, fragment, "-") If P > 0 Then 'yes, so it's a range: find start and end numbers nstart = Val(left$(fragment, P - 1)) nend = Val(Mid$(fragment, P + 1)) j = count count = count + (nend - nstart + 1) 'add numbers in range to List ReDim Preserve List(count) For i = nstart To nend j = j + 1 List(j) = i Next Else 'not a range, add a single number count = count + 1 ReDim Preserve List(count) List(count) = Val(fragment) End If
Next RangeExpand = List End Function
Public Sub RangeExpandTest() 'test function RangeExpand Dim X As Variant
X = RangeExpand("-6,-3--1,3-5,7-11,14,15,17-20") 'print X Debug.Print "Result:" For Each el In X
Debug.Print el;
Next Debug.Print End Sub</lang>
- Output:
RangeExpandTest Result: -6 -3 -2 -1 3 4 5 7 8 9 10 11 14 15 17 18 19 20
XPL0
<lang XPL0>include c:\cxpl\codes; \intrinsic 'code' declarations string 0; \use zero-terminated strings, instead of MSb char Str; int Char, Inx;
proc GetCh; \Get character from Str
[Char:= Str(Inx);
Inx:= Inx+1;
]; \GetCh
func GetNum; \Get number from Str and return its value
int Neg, Num;
[Neg:= false;
if Char = ^- then [Neg:= true; GetCh];
Num:= 0;
while Char>=^0 & Char<=^9 do
[Num:= Num*10 + Char-^0; GetCh; ];
return if Neg then -Num else Num; ]; \GetNum
int I, N0, N1;
[Str:= "-6,-3--1,3-5,7-11,14,15,17-20";
Inx:= 0;
GetCh; \one character look ahead
loop [N0:= GetNum;
IntOut(0,N0); case Char of ^,: [GetCh; ChOut(0,^,)]; ^-: [GetCh; N1:= GetNum; for I:= N0+1 to N1 do \expand range [ChOut(0,^,); IntOut(0,I)]; if Char=^, then [GetCh; ChOut(0,^,)] else quit] other quit; \must be 0 string terminator ];
CrLf(0); ]</lang>
Output:
-6,-3,-2,-1,3,4,5,7,8,9,10,11,14,15,17,18,19,20
- Programming Tasks
- Solutions by Programming Task
- Ada
- ALGOL 68
- ALGOL 68 examples needing attention
- Examples needing attention
- AutoHotkey
- Bracmat
- C
- C sharp
- C++
- Clojure
- Common Lisp
- D
- F Sharp
- Go
- Groovy
- Haskell
- Icon
- Unicon
- J
- Java
- K
- Liberty BASIC
- Mathematica
- MUMPS
- OCaml
- Oz
- Perl
- Perl 6
- PHP
- PicoLisp
- PL/I
- Prolog
- Clpfd
- PureBasic
- Python
- REXX
- Ruby
- Scala
- Scheme
- Seed7
- SNOBOL4
- Tcl
- TUSCRIPT
- Ursala
- VBA
- XPL0