Range expansion: Difference between revisions

From Rosetta Code
Content added Content deleted
(+Common Lisp)
(Added K Version)
Line 660: Line 660:
}
}
}</lang>
}</lang>

=={{header|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>


=={{header|MUMPS}}==
=={{header|MUMPS}}==

Revision as of 09:53, 30 June 2011

Task
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> Sample output:

-6,-3,-2,-1, 3, 4, 5, 7, 8, 9, 10, 11, 14, 15, 17, 18, 19, 20

ALGOL 68

This example is incorrect. Please fix the code and remove this message.

Details: The task example was modified after discussion in the talk page.

Works with: ALGOL 68 version Revision 1 - no extensions to language used
Works with: ALGOL 68G version Any - tested with release 1.18.0-9h.tiny
Works with: ELLA ALGOL 68 version Any (with appropriate job cards) - tested with release 1.8-8d

<lang algol68>MODE YIELDINT = PROC(INT)VOID;

MODE RANGE = STRUCT(INT lwb, upb); MODE RANGEINT = UNION(RANGE, INT);

OP SIZEOF = ([]RANGEINT list)INT: (

  1. 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;
  1. FOR INT value IN # gen range expand(list, # ) DO #
    1. (INT value)VOID:
   out[upb +:= 1] := value
  1. 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

C

<lang c>#include <stdio.h>

  1. include <stdlib.h>
  2. include <string.h>
  3. include <stdarg.h>
  4. include <stdbool.h>

enum range_action { RANGE_NEW, RANGE_NEXT, RANGE_RESET, RANGE_FREE };

typedef struct range_s {

 long int val;
 char *range;   // private members
 size_t pos;
 size_t slen;
 long int clower;
 long int cupper;
 bool inrange;

} *range_t;

range_t range(enum range_action action, ...) {

 range_t r;
 va_list al;
 char *ep, *tp;
 va_start(al, action);
 switch(action) {
 case RANGE_NEW:
   r = malloc(sizeof(struct range_s));
   
   if (r != NULL) {
     r->range = va_arg(al, char *);
     r->pos = 0;
     r->slen = strlen(r->range);
     r->inrange = false;
   }
   break;
 case RANGE_NEXT:
   r = va_arg(al, range_t);
   if ( r->inrange ) {
     if ( r->val < r->cupper ) {

r->val++; va_end(al); return r;

     } else {

r->inrange = false;

     }
   }
   if ( r->pos >= r->slen ) return NULL;
   r->val = strtol(r->range + r->pos, &ep, 10);
   if ( ep == (r->range + r->pos) ) return NULL;
   if ( *ep == '-' ) {
     r->clower = r->val;
     r->cupper = strtol(ep + 1, &tp, 10);
     if ( (ep+1) == tp ) return NULL;
     r->inrange = true;
     ep = tp;
   } // any other symbol works like ,
   r->pos = ep+1 - r->range;
   break;
 case RANGE_RESET:
   r = va_arg(al, range_t);
   r->pos = 0;
   break;
 case RANGE_FREE:
   r = va_arg(al, range_t);
   free(r);
   return NULL;
 }
 va_end(al);
 return r;

}

// usage int main() {

 range_t r = range(RANGE_NEW, "-6,-3--1,3-5,7-11,14,15,17-20");
 while( range(RANGE_NEXT, r) != NULL )
 {
   printf("%d ", r->val);
 }
 printf("\n");
 range(RANGE_FREE, r);
 return 0;

}</lang>

C#

Works with: C sharp version 3.0

<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>

  1. include <iostream>
  2. include <sstream>
  3. include <iterator>
  4. include <climits>
  5. 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

Compiled with D V.2.048. From the second Python version. <lang d>import std.stdio, std.regex, std.string, std.conv, std.range;

int[] rangeExpand(string txt) {

 int[] result;
 foreach (r; txt.split(",")) {
   auto m = array(match(r, regex(r"^(-?\d+)(-?(-?\d+))?$")).captures);
   if (m.length > 2)
     result ~= array(iota(to!int(m[1]), to!int(m[3])+1));
   else
     result ~= to!int(m[1]);
 }
 return result;

}

void main() {

 writeln(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]


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, ",", -1) {
       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>

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>

Sample 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;
   }
   if ( inrange ) 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>

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 (in Oz list syntax): <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

}

  1. 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

Based on Python version.

<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 <lang>

 -6, -3, -2, -1,  3,  4,  5,  7,  8,  9, 10, 11, 14, 15, 17, 18, 19, 20

</lang>

Prolog

Works with SWI-Prolog and library clpfd.
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> Sample 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>

Sample 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>

(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

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

  1. import int

rex = sep`,; zrange+*= %zp~~htttPzztPQhQXbiNC+ rlc ~&r~=`-

  1. 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>