Loops/Wrong ranges: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|REXX}}: added zkl header)
(→‎{{header|zkl}}: added zode)
Line 423: Line 423:


=={{header|zkl}}==
=={{header|zkl}}==
<lang zkl>// zero increment (ie infnite loop) throws an error
<lang zkl></lang>
// if stop is "*", the loop is has no end (ie infinite)
<lang zkl></lang>
// 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));
if(increment!=0){ foreach n in ([start..stop,increment]){ print(n," ") } }
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);</lang>
{{out}}
{{out}}
<pre>
<pre>
start stop increment
-2 2 1 --> -2 -1 0 1 2
-2 2 0 -->
-2 2 -1 -->
-2 2 10 --> -2
2 -2 1 -->
2 2 1 --> 2
2 2 -1 --> 2
2 2 0 -->
0 0 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 -->
</pre>
</pre>

Revision as of 17:33, 16 September 2018

Loops/Wrong ranges is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

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

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]

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: +*.so

}

  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 through strings.

put 'a' … 'h';</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 | 
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])
a b c d e f g h

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));
  if(increment!=0){ foreach n in ([start..stop,increment]){ print(n," ") } }
  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);</lang>

Output:
start stop  increment
  -2    2	 1 --> -2 -1 0 1 2 
  -2    2	 0 --> 
  -2    2	-1 --> 
  -2    2	10 --> -2 
   2   -2	 1 --> 
   2    2	 1 --> 2 
   2    2	-1 --> 2 
   2    2	 0 --> 
   0    0	 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 -->