While

Slang includes the following loop constructs from Scala: while, do ... while, for (with iterators over Slang data structures and for comprehensions).

while loops are written as in Scala, except that a loop body must always be a block:

   // while loop

   var i = 0
   while (i < 10) { // a while-loop requires a block for its body
     print(i, " ")
     i = i + 1
   }
   println()

Do While

do .. while is similar:

   // do-while loop

   do { // a do-while-loop requires a block for its body
     i = i - 1
     print(i, " ")
   } while (i > 0)
   println()

Note

From John, To Robby is the C/Java 3-part for-loop supported (initialize, increment, bound)

For Loops

For Loops with Range Generators

Note

ToDo, From John, To Robby What is the essence of restrictions on Scala for loops in Slang?

The simplest variations of the Slang/Scala for loops have the following structure (as with the other Slang loop forms, a block is required for a loop body):


   for (variable <- range) {
     statements             // a for-loop requires a block for its body
   }

The are executed for each value in the , and binds to the current value on each iteration. The may be stated in different ways. For numeric values, the range is constructed using a Scala generator – a construct that generates a sequence of values.

Note

ToDo, From John: I don’t have the terminology exactly right for the Scala concept of generator, comprehensions, etc. I need to review Scala doc in detail then tweak this section.**

ToDo, To John, To Robby: probably need to do a more complete job connecting to the concept of a Slang range (ZRange). Make connection to Scala ranges https://alvinalexander.com/scala/how-to-use-range-class-in-scala-cookbook

Below are some for loops with different types of generators. First, an until is used to create a sequence starting with the lower bound up to (but not including) the upper bound:


   // for loop with "until"  (excludes upper bound)

   for (j <- 0 until 10) { // "until" excludes the upper bound
     print(j, " ")
   }
   println()

Alternatively, a to is used to create a sequence that includes the upper bound:

   // for loop with "to"  (includes upper bound)

   for (j <- 0 to 12) { // "to" includes the upper bound
     print(j, " ")
   }
   println()

The by construct can be used with both until and to to specify “steps” in the generated sequence other than the default +1:

   // for loops with "by"  (specifies "steps")

   for (j <- 0 to 12 by 3) { // use "by" to specify increments of +3
     print(j, " ")
   }
   println()

   for (j <- 100 until 0 by -5) { // use "by" to specify increments of -5
     print(j, " ")
   }
   println()

The code above produces the following output:

   0 3 6 9 12
   100 95 90 85 80 75 70 65 60 55 50 45 40 35 30 25 20 15 10 5

The if construct can be used to further filter the values returned in the generated sequence by only including values satisfying the condition specified in the if clause. For example, the generator in the following loop only produces values that are multiples of 10:


   // for loop with "if" to further filter values considered in loop

   for (j <- 100 until 0 by -5 if j % 10 == 0) { // use "if" to further filter
     print(j, " ")
   }
   println()

The following output is produced:

   100 90 80 70 60 50 40 30 20 10

Using Multiple Iterator Variables and Generators

As in Scala, multiple generators can be combined in a for statement. For example, the following code has the effect of a nested loop, with the outer loop iterating with j and the inner loop iterating with k:

   // for loop with multiple generators (abbreviates nested loop)

   for (j <- 0 to 50 by 10; k <- 1 to 3) { // multiple generators abbreviate nested loops
     print(j + k, " ")
   }
   println()

The following output is produced:

 1 2 3 11 12 13 21 22 23 31 32 33 41 42 43 51 52 53

Note

ToDo From John, To Robby: Generators apply to what types? Only integers? is j of type Z in examples above?

Iterating Over Slang Collections

Slang collection types support iterators for for loops. In the following example, e binds to each element of the indexed sequence of characters s1:

   // iterating over Slang sequences (ISZ)

   val s1 = ISZ("a", "b", "c")
   for (e <- s1) { // iterating over all elements of s1
     println(e)
   }

Illustrations of iterating over more complex Slang type are provided in

Note

Todo: To John: Insert reference to Slang data structures chapter.

For Comprehensive with Slang Collections

Slang also supports Scala’s for comprehension notation, which is often used when programming in a functional style. In the example below, the result sequence s3 is constructed in a functional style from s2:

   // for loop with comprehension (yield)

   val s2 = ISZ('w', 'x', 'y', 'z')
   val s3: ISZ[(Z, C)] = // result value is a sequence of Z,C pairs
     for (j <- s2.indices) yield (j, s2(j)) // for comprehension notation
   println(s3)

The following output is produced:

   [(0,w), (1,x), (2,y), (3,z)]

First, s2.indices constructs a sequence of the indices of s2 (indexing is zero-based). Then, each element in the sequence of indices is transformed into a pair consisting of the index (j) and the s2 value at that index position s2(j). The result values specified by the yield expression (in this case, pairs) are collected into a sequence. Readers familiar with functional programming or collections operations in other languages will under that the pattern above is similar to the map functional programming concept where yield specifies the result expression for values being “re-collected” in the final sequence. Note that the block notation { ... } is not used with for comprehension.

Iterating Over Slang Collections – Complex Examples

Here are some more complex examples:


   // comprehension for Slang sequences

   val s4 = ISZ("a", "b", "c")
   val s5 = ISZ(1, 2, 3)
   val s6 = ISZ(T, F)
   println()
   println("[a, b, c] x [1, 2, 3] x [T, F] = ", for (e4 <- s4; e5 <- s5; e6 <- s6) yield (e4, e5, e6))
   println()
   println(for (e4 <- s4; e5 <- s5; e6 <- s6 if (e5 != 1) || e6) yield (e4, e5, e6))

The following output is produced (with some reformatting to wrap and indent for readability):


   [a, b, c] x [1, 2, 3] x [T, F] =
    [(a,1,true), (a,1,false),
     (a,2,true), (a,2,false),
     (a,3,true), (a,3,false),
     (b,1,true), (b,1,false),
     (b,2,true), (b,2,false),
     (b,3,true), (b,3,false),
     (c,1,true), (c,1,false),
     (c,2,true), (c,2,false),
     (c,3,true), (c,3,false)]

    [(a,1,true),
     (a,2,true), (a,2,false),
     (a,3,true), (a,3,false),
     (b,1,true),
     (b,2,true), (b,2,false),
     (b,3,true), (b,3,false),
     (c,1,true),
     (c,2,true), (c,2,false),
     (c,3,true), (c,3,false)]

In the first example, multiple iterators are used in a single for to create all combinations of values in the sequences s4, s5, s6. In the second example, an if is used to filter the generated sequence fed to the yield construct – only combinations where the s5 element is not 1 or the s6 element is T are passed on to the yield expression.

See the Scala documentation for details of how yield is expressed in terms of more basic operations including foreach, map, flatMap, and filter.

Note

ToDo: To John: What’s the relationship between the type of the for argument, type of yield expression, and type of result expression?