Loops/Wrong ranges: Difference between revisions

From Rosetta Code
Content added Content deleted
(Promote to full task status.)
Line 1: Line 1:
{{draft task}}
{{task}}


Some languages have syntax or function(s) to generate a range of numeric values from a start value, a stop value, and an increment.
Some languages have syntax or function(s) to generate a range of numeric values from a start value, a stop value, and an increment.

Revision as of 19:50, 16 September 2018

Task
Loops/Wrong ranges
You are encouraged to solve this task according to the task description, using any language you may know.

Some languages have syntax or function(s) to generate a range of numeric values from a start value, a stop value, and an increment.

The purpose of this task is to select the range syntax/function that would generate at least two increasing numbers when given a stop value more than the start value and a positive increment of less than half the difference. You are than to use that same syntax/function but with different parameters; and show, here, what would happen.

Use these values if possible:

start stop increment Comment
-2 2 1 Normal
-2 2 0 Zero increment
-2 2 -1 Increments away from stop value
-2 2 10 First increment is beyond stop value
2 -2 1 Start more than stop: positive increment
2 2 1 Start equal stop: positive increment
2 2 -1 Start equal stop: negative increment
2 2 0 Start equal stop: zero increment
0 0 0 Start equal stop equal zero: zero increment

C

C's 'for' statement appears to fit the bill here and so we use it directly to generate the required ranges of numbers though, as some of the ranges will be infinite, we limit the output to a maximum of 10 numbers. <lang c>#include <stdio.h>

  1. define TRUE 1
  2. define FALSE 0

typedef int bool;

typedef struct {

   int start, stop, incr;
   const char *comment;

} S;

S examples[9] = {

   {-2, 2, 1, "Normal"},
   {-2, 2, 0, "Zero increment"},
   {-2, 2, -1, "Increments away from stop value"},
   {-2, 2, 10, "First increment is beyond stop value"},
   {2, -2, 1, "Start more than stop: positive increment"},
   {2, 2, 1, "Start equal stop: positive increment"},
   {2, 2, -1, "Start equal stop: negative increment"},
   {2, 2, 0, "Start equal stop: zero increment"},
   {0, 0, 0, "Start equal stop equal zero: zero increment"}

};

int main() {

   int i, j, c;
   bool empty;
   S s;
   const int limit = 10;
   for (i = 0; i < 9; ++i) {
       s = examples[i];
       printf("%s\n", s.comment);
       printf("Range(%d, %d, %d) -> [", s.start, s.stop, s.incr);
       empty = TRUE;
       for (j = s.start, c = 0; j <= s.stop && c < limit; j += s.incr, ++c) {
           printf("%d ", j);
           empty = FALSE;
       }
       if (!empty) printf("\b");
       printf("]\n\n");
   }
   return 0;

}</lang>

Output:
Normal
Range(-2, 2, 1) -> [-2 -1 0 1 2]

Zero increment
Range(-2, 2, 0) -> [-2 -2 -2 -2 -2 -2 -2 -2 -2 -2]

Increments away from stop value
Range(-2, 2, -1) -> [-2 -3 -4 -5 -6 -7 -8 -9 -10 -11]

First increment is beyond stop value
Range(-2, 2, 10) -> [-2]

Start more than stop: positive increment
Range(2, -2, 1) -> []

Start equal stop: positive increment
Range(2, 2, 1) -> [2]

Start equal stop: negative increment
Range(2, 2, -1) -> [2 1 0 -1 -2 -3 -4 -5 -6 -7]

Start equal stop: zero increment
Range(2, 2, 0) -> [2 2 2 2 2 2 2 2 2 2]

Start equal stop equal zero: zero increment
Range(0, 0, 0) -> [0 0 0 0 0 0 0 0 0 0]

Go

Go has only one loop, a 'for' statement, which supports four different syntactical forms commonly found in other C-family languages:

1. A C-like 'for' loop with initialization, condition and increment sections.

2. The 'while' loop functionality (condition only)

3. Infinite loop, equivalent to for(;;) (all sections omitted)

4. Looping over a range of values, similar to foreach etc. (using 'range' keyword).

It appears that either #1 or #4 fits the requirements of this task so I've written a function which generates the appropriate sequence using #1 (limited to a maximum of 10 elements as some sequences will be infinite). I've then applied #4 to the resulting sequence. All sequences include the stop value if it's actually reached. <lang go>package main

import "fmt"

type S struct {

   start, stop, incr int
   comment          string

}

var examples = []S{

   {-2, 2, 1, "Normal"},
   {-2, 2, 0, "Zero increment"},
   {-2, 2, -1, "Increments away from stop value"},
   {-2, 2, 10, "First increment is beyond stop value"},
   {2, -2, 1, "Start more than stop: positive increment"},
   {2, 2, 1, "Start equal stop: positive increment"},
   {2, 2, -1, "Start equal stop: negative increment"},
   {2, 2, 0, "Start equal stop: zero increment"},
   {0, 0, 0, "Start equal stop equal zero: zero increment"},

}

func sequence(s S, limit int) []int {

   var seq []int
   for i, c := s.start, 0; i <= s.stop && c < limit; i, c = i+s.incr, c+1 {
       seq = append(seq, i)
   }
   return seq

}

func main() {

   const limit = 10
   for _, ex := range examples {
       fmt.Println(ex.comment)
       fmt.Printf("Range(%d, %d, %d) -> ", ex.start, ex.stop, ex.incr)
       fmt.Println(sequence(ex, limit))
       fmt.Println()
   }

}</lang>

Output:
Normal
Range(-2, 2, 1) -> [-2 -1 0 1 2]

Zero increment
Range(-2, 2, 0) -> [-2 -2 -2 -2 -2 -2 -2 -2 -2 -2]

Increments away from stop value
Range(-2, 2, -1) -> [-2 -3 -4 -5 -6 -7 -8 -9 -10 -11]

First increment is beyond stop value
Range(-2, 2, 10) -> [-2]

Start more than stop: positive increment
Range(2, -2, 1) -> []

Start equal stop: positive increment
Range(2, 2, 1) -> [2]

Start equal stop: negative increment
Range(2, 2, -1) -> [2 1 0 -1 -2 -3 -4 -5 -6 -7]

Start equal stop: zero increment
Range(2, 2, 0) -> [2 2 2 2 2 2 2 2 2 2]

Start equal stop equal zero: zero increment
Range(0, 0, 0) -> [0 0 0 0 0 0 0 0 0 0]

Huginn

Huginn has the Range generator in Algorithms package. Instantiation of an a priori invalid range is a fatal error.

<lang huginn>import Algorithms as algo;

class Example {

 _start = none;
 _stop = none;
 _step = none;
 _comment = none;

}

main() {

 examples = [
   Example( -2,  2,  1, "Normal" ),
   Example(  2,  2,  0, "Start equal stop: zero increment" ),
   Example(  0,  0,  0, "Start equal stop equal zero: zero increment" ),
   Example(  2,  2,  1, "Start equal stop: positive increment" ),
   Example(  2,  2, -1, "Start equal stop: negative increment" ),
   Example( -2,  2, 10, "First increment is beyond stop value" ),
   Example( -2,  2,  0, "Zero increment, stop greater than start" ),
   Example( -2,  2, -1, "Increments away from stop value" ),
   Example(  2, -2,  1, "Start more than stop: positive increment" )
 ];
 for ( ex : examples ) {
   print(
     "{}\nRange( {}, {}, {} ) -> ".format(
       ex._comment, ex._start, ex._stop, ex._step
     )
   );
   r = algo.range( ex._start, ex._stop, ex._step );
   print(
     "{}\n\n".format(
       algo.materialize( algo.slice( r, 22 ), list )
     )
   );
 }

}</lang>

Output:
Normal
Range( -2, 2, 1 ) -> [-2, -1, 0, 1]

Start equal stop: zero increment
Range( 2, 2, 0 ) -> []

Start equal stop equal zero: zero increment
Range( 0, 0, 0 ) -> []

Start equal stop: positive increment
Range( 2, 2, 1 ) -> []

Start equal stop: negative increment
Range( 2, 2, -1 ) -> []

First increment is beyond stop value
Range( -2, 2, 10 ) -> [-2]

Zero increment, stop greater than start
Range( -2, 2, 0 ) -> ./range.hgn:32:17: Invalid range.
Exit 3

Kotlin

Although Kotlin's 'for' statement can deal with a range of integers, the increment must be positive and so it cannot be used for this task. We therefore use instead a 'while' statement to generate the same sequence as a C language 'for' statement would (limited to a maximum of 10 elements as some sequences will be infinite) and wrap it in a function. <lang scala>// Version 1.2.70

class Example(val start: Int, val stop: Int, val incr: Int, val comment: String)

var examples = listOf(

   Example(-2, 2, 1, "Normal"),
   Example(-2, 2, 0, "Zero increment"),
   Example(-2, 2, -1, "Increments away from stop value"),
   Example(-2, 2, 10, "First increment is beyond stop value"),
   Example(2, -2, 1, "Start more than stop: positive increment"),
   Example(2, 2, 1, "Start equal stop: positive increment"),
   Example(2, 2, -1, "Start equal stop: negative increment"),
   Example(2, 2, 0, "Start equal stop: zero increment"),
   Example(0, 0, 0, "Start equal stop equal zero: zero increment")

)

fun sequence(ex: Example, limit: Int) =

   if (ex.incr == 0) {
       List(limit) { ex.start }
   }
   else {
       val res = mutableListOf<Int>()
       var c = 0
       var i = ex.start
       while (i <= ex.stop && c < limit) {
           res.add(i)
           i += ex.incr
           c++
       }
       res
   }

fun main(args: Array<String>) {

   for (ex in examples) {
       println(ex.comment)
       System.out.printf("Range(%d, %d, %d) -> ", ex.start, ex.stop, ex.incr)
       println(sequence(ex, 10))
       println()
   }

}</lang>

Output:
Normal
Range(-2, 2, 1) -> [-2, -1, 0, 1, 2]

Zero increment
Range(-2, 2, 0) -> [-2, -2, -2, -2, -2, -2, -2, -2, -2, -2]

Increments away from stop value
Range(-2, 2, -1) -> [-2, -3, -4, -5, -6, -7, -8, -9, -10, -11]

First increment is beyond stop value
Range(-2, 2, 10) -> [-2]

Start more than stop: positive increment
Range(2, -2, 1) -> []

Start equal stop: positive increment
Range(2, 2, 1) -> [2]

Start equal stop: negative increment
Range(2, 2, -1) -> [2, 1, 0, -1, -2, -3, -4, -5, -6, -7]

Start equal stop: zero increment
Range(2, 2, 0) -> [2, 2, 2, 2, 2, 2, 2, 2, 2, 2]

Start equal stop equal zero: zero increment
Range(0, 0, 0) -> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Perl 6

Works with: Rakudo version 2018.08

It would be odd to call ANY of these sequences "wrong" in Perl 6. Perl 6 specifically has built in capability of working with infinite sequences. Just because a sequence is infinite, doesn't mean you can't define it, work with it or use values from it. Sure, if you try to reify the whole thing you may be waiting a while, but there is nothing preventing you from using a portion of it.

Perl 6 sequence definitions specifically allow "ending points" that may never occur in the sequence. Since that is the case, you don't even really need to specify a stop value. You can just say stop at "whatever". Whatever is spelled "*" in Perl 6.

There is additional syntax you can add to stop at the nearest value, last value previous or first value successor to the "stop value" (Note I didn't say less than or greater than the stop value since the sequence can be ascending, descending or non-monotonic).

Also note: The iterator function for the sequence is literally a function. It is any expression that produces a value. These sequences all use simple arithmatic increments but that is not a limitation of the sequence operator.

<lang perl6># Given sequence definitions

  1. start stop inc. Comment

for -2, 2, 1, # Normal

     -2,    2,    0, # Zero increment
     -2,    2,   -1, # Increments away from stop value
     -2,    2,   10, # First increment is beyond stop value
      2,   -2,    1, # Start more than stop: positive increment
      2,    2,    1, # Start equal stop: positive increment
      2,    2,    0, # Start equal stop: zero increment
      0,    0,    0, # Start equal stop equal zero: zero increment
  1. Additional "problematic" sequences
      1,  Inf,    3, # Endpoint literally at infinity
      0,    π,  τ/8, # Floating point numbers
    1.4,    *, -7.1  # Whatever
 -> $start, $stop, $inc {
   my $seq = flat ($start, *+$inc … $stop);
   printf "Start: %3s, Stop: %3s, Increment: %3s | ", $start, $stop.Str, $inc;
   # only show up to the first 15 elements of possibly infinite sequences
   put $seq[^15].grep: +*.defined

}

  1. For that matter the start and end values don't need to be numeric either. Both
  2. or either can be a function, list, or other object. Really anything that a
  3. "successor" function can be defined for and produces a value.
  1. Start with a list, iterate by multiplying the previous 3 terms together
  2. and end with a term defined by a function.

put 1, -.5, 2.sqrt, * * * * * … *.abs < 1e-2;

  1. Start with an array, iterate by rotating, end when 0 is in the last place.

say [0,1,2,3,4,5], *.rotate(-1) … !*.tail;

  1. Iterate strings backwards.

put 'xp' … 'xf';</lang>

Output:
Start:  -2, Stop:   2, Increment:   1 | -2 -1 1 2
Start:  -2, Stop:   2, Increment:   0 | -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2
Start:  -2, Stop:   2, Increment:  -1 | -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16
Start:  -2, Stop:   2, Increment:  10 | -2 8 18 28 38 48 58 68 78 88 98 108 118 128 138
Start:   2, Stop:  -2, Increment:   1 | 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Start:   2, Stop:   2, Increment:   1 | 2
Start:   2, Stop:   2, Increment:   0 | 2
Start:   0, Stop:   0, Increment:   0 | 0
Start:   1, Stop: Inf, Increment:   3 | 1 4 7 10 13 16 19 22 25 28 31 34 37 40 43
Start:   1, Stop:   2, Increment:   0 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
Start:   0, Stop: 3.141592653589793, Increment: 0.7853981633974483 | 0.7853981633974483 1.5707963267948966 2.356194490192345 3.141592653589793
Start: 1.4, Stop:   *, Increment: -7.1 | 1.4 -5.7 -12.8 -19.9 -27 -34.1 -41.2 -48.3 -55.4 -62.5 -69.6 -76.7 -83.8 -90.9 -98
1 -0.5 1.4142135623730951 -0.7071067811865476 0.5000000000000001 -0.5000000000000002 0.176776695296637 -0.04419417382415928 0.0039062500000000095
([0 1 2 3 4 5] [5 0 1 2 3 4] [4 5 0 1 2 3] [3 4 5 0 1 2] [2 3 4 5 0 1] [1 2 3 4 5 0])
xp xo xn xm xl xk xj xi xh xg xf

Python

Python has the range function. <lang python>import re from itertools import islice # To limit execution if it would generate huge values

  1. list(islice('ABCDEFG', 2)) --> ['A', 'B']
  2. list(islice('ABCDEFG', 4)) --> ['A', 'B', 'C', 'D']


data = start stop increment Comment -2 2 1 Normal -2 2 0 Zero increment -2 2 -1 Increments away from stop value -2 2 10 First increment is beyond stop value 2 -2 1 Start more than stop: positive increment 2 2 1 Start equal stop: positive increment 2 2 -1 Start equal stop: negative increment 2 2 0 Start equal stop: zero increment 0 0 0 Start equal stop equal zero: zero increment

table = [re.split(r'\s\s+', line.strip()) for line in data.strip().split('\n')]

  1. %%

for _start, _stop, _increment, comment in table[1:]:

   start, stop, increment = [int(x) for x in (_start, _stop, _increment)]
   print(f'{comment.upper()}:\n  range({start}, {stop}, {increment})')
   error, values = None, None
   try: 
       values = list(islice(range(start, stop, increment), 999))
   except ValueError as e:
       error = e
       print('  !!ERROR!!', e)
   if values is not None:
       if len(values) < 22:
           print('    =', values)
       else:
           print('    =', str(values[:22])[:-1], '...')

</lang>

Output:
NORMAL:
  range(-2, 2, 1)
    = [-2, -1, 0, 1]
ZERO INCREMENT:
  range(-2, 2, 0)
  !!ERROR!! range() arg 3 must not be zero
INCREMENTS AWAY FROM STOP VALUE:
  range(-2, 2, -1)
    = []
FIRST INCREMENT IS BEYOND STOP VALUE:
  range(-2, 2, 10)
    = [-2]
START MORE THAN STOP: POSITIVE INCREMENT:
  range(2, -2, 1)
    = []
START EQUAL STOP: POSITIVE INCREMENT:
  range(2, 2, 1)
    = []
START EQUAL STOP: NEGATIVE INCREMENT:
  range(2, 2, -1)
    = []
START EQUAL STOP: ZERO INCREMENT:
  range(2, 2, 0)
  !!ERROR!! range() arg 3 must not be zero
START EQUAL STOP EQUAL ZERO: ZERO INCREMENT:
  range(0, 0, 0)
  !!ERROR!! range() arg 3 must not be zero

REXX

Note that a do loop with zero by value, or a do loop that goes in the "wrong" direction is not considered an error in REXX as there are other methods of limiting the range (or stopping condition) within the loop body.   A special check was made in this REXX version to check for a runaway (race) condition. <lang rexx>/*REXX program demonstrates several versions of DO loops with "unusual" interations. */ @.=; @.1= ' -2 2 1 ' /*"normal". */

         @.2=  '  -2      2       0  '      /*"normal",                zero  increment.*/
         @.3=  '  -2      2      -1  '      /*increases away from stop, neg  increment.*/
         @.4=  '  -2      2      10  '      /*1st increment > stop, positive increment.*/
         @.5=  '   2     -2       1  '      /*start > stop,         positive increment.*/
         @.6=  '   2      2       1  '      /*start equals stop,    positive increment.*/
         @.7=  '   2      2      -1  '      /*start equals stop,    negative increment.*/
         @.8=  '   2      2       0  '      /*start equals stop,       zero  increment.*/
         @.9=  '   0      0       0  '      /*start equals stop,       zero  increment.*/

zLim= 10 /*a limit to check for runaway (race) loop.*/

                                            /*a zero increment is not an error in REXX.*/
 do k=1  while  @.k\==                    /*perform a  DO  loop with several ranges. */
 parse var   @.k    x  y  z  .              /*obtain the three values for a DO loop.   */
 say
 say center('start of performing DO loop number '   k   " with range: "  x y z,  79, '═')
 zz= 0
       do  j=x   to y   by z   until zz>=zLim           /* ◄───  perform the  DO  loop.*/
       say '   j ───►'  right(j, max(3, length(j) ) )   /*right justify J for alignment*/
       if z==0  then zz= zz + 1                         /*if zero inc, count happenings*/
       end   /*j*/
 if zz>=zLim  then say 'the DO loop for the '    k    " entry was terminated (runaway)."
 say center(' end  of performing DO loop number '   k   " with range: "  x y z,  79, '─')
 say
 end         /*k*/                              /*stick a fork in it,  we're all done. */</lang>
output:
══════════start of performing DO loop number  1  with range:  -2 2 1═══════════
   j ───►  -2
   j ───►  -1
   j ───►   0
   j ───►   1
   j ───►   2
────────── end  of performing DO loop number  1  with range:  -2 2 1───────────


══════════start of performing DO loop number  2  with range:  -2 2 0═══════════
   j ───►  -2
   j ───►  -2
   j ───►  -2
   j ───►  -2
   j ───►  -2
   j ───►  -2
   j ───►  -2
   j ───►  -2
   j ───►  -2
   j ───►  -2
the DO loop for the  2  entry was terminated (runaway).
────────── end  of performing DO loop number  2  with range:  -2 2 0───────────


══════════start of performing DO loop number  3  with range:  -2 2 -1══════════
────────── end  of performing DO loop number  3  with range:  -2 2 -1──────────


══════════start of performing DO loop number  4  with range:  -2 2 10══════════
   j ───►  -2
────────── end  of performing DO loop number  4  with range:  -2 2 10──────────


══════════start of performing DO loop number  5  with range:  2 -2 1═══════════
────────── end  of performing DO loop number  5  with range:  2 -2 1───────────


═══════════start of performing DO loop number  6  with range:  2 2 1═══════════
   j ───►   2
─────────── end  of performing DO loop number  6  with range:  2 2 1───────────


══════════start of performing DO loop number  7  with range:  2 2 -1═══════════
   j ───►   2
────────── end  of performing DO loop number  7  with range:  2 2 -1───────────


═══════════start of performing DO loop number  8  with range:  2 2 0═══════════
   j ───►   2
   j ───►   2
   j ───►   2
   j ───►   2
   j ───►   2
   j ───►   2
   j ───►   2
   j ───►   2
   j ───►   2
   j ───►   2
the DO loop for the  8  entry was terminated (runaway).
─────────── end  of performing DO loop number  8  with range:  2 2 0───────────


═══════════start of performing DO loop number  9  with range:  0 0 0═══════════
   j ───►   0
   j ───►   0
   j ───►   0
   j ───►   0
   j ───►   0
   j ───►   0
   j ───►   0
   j ───►   0
   j ───►   0
   j ───►   0
the DO loop for the  9  entry was terminated (runaway).
─────────── end  of performing DO loop number  9  with range:  0 0 0───────────

zkl

<lang zkl>// zero increment (ie infnite loop) throws an error // if stop is "*", the loop is has no end (ie infinite) // stop is included unless step steps skips it // if start > stop is a dead loop // ranges ([a..b,c]) are lazy lists fcn looper([(start,stop,increment)]){

  print(" %3s  %3s\t%2d --> ".fmt(start,stop,increment));
  try{ foreach n in ([start..stop,increment]){ print(n," ") } }
  catch{ print(__exception) }
  println();

} println("start stop increment"); T( T(-2,2,1),T(-2,2,0),T(-2,2,-1),T(-2,2,10),T( 2,-2,1),

  T( 2,2,1),T( 2,2,-1),T( 2,2,0),T( 0,0,0), 
  T(0.0, (0.0).pi, 0.7853981633974483), T("a","e",1), T("e","a",1) )

.apply2(looper); // apply2 is apply (map) without saving results</lang>

Output:
start stop  increment
  -2    2	 1 --> -2 -1 0 1 2 
  -2    2	 0 --> ValueError(range: step == 0)
  -2    2	-1 --> 
  -2    2	10 --> -2 
   2   -2	 1 --> 
   2    2	 1 --> 2 
   2    2	-1 --> 2 
   2    2	 0 --> ValueError(range: step == 0)
   0    0	 0 --> ValueError(range: step == 0)
   0  3.14159	 0 --> 0 0.785398 1.5708 2.35619 3.14159 
   a    e	 1 --> a b c d e 
   e    a	 1 -->