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