Range expansion: Difference between revisions
(→{{header|J}}: Existing code & example correct AFAICS) |
(→{{header|ALGOL 68}}: Added C++) |
||
Line 61: | Line 61: | ||
-6 -3 -2 -1 +0 +1 +3 +4 +5 +7 +8 +9 +10 +11 +14 +15 +17 +18 +19 +20 |
-6 -3 -2 -1 +0 +1 +3 +4 +5 +7 +8 +9 +10 +11 +14 +15 +17 +18 +19 +20 |
||
</pre> |
</pre> |
||
=={{header|C++}}== |
|||
<lang c++> |
|||
#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> |
|||
=={{header|J}}== |
=={{header|J}}== |
Revision as of 09:06, 17 July 2010
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
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
C++
<lang c++>
- 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>
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, 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