Loops and Flow Control
Conditional Branches
if else expressions are expressions in Gambol meaning that they can be evaluated to a value. For example:
x = 12
y = 100 if x < 12 else 3.5
The above shows the syntax for inline conditionals. The type a conditional is
evaluated to is determined by the type of the first branch that is not disabled
(more on disabled branches later). In the above example the inferred type for y
is going to be NInt
even if the second branch of the conditional returns a
floating point value.
Here is an example for conditionals with multiple branches:
if x == 12 then
print(`x == 12`)
else if x < 5 then
print(`x < 5`)
else
print(`x > 20`)
end
The conditional expression must be assignable to Bool
.
Lexical Scope
The branches in if-else expressions do not open a new scope. If you like though you could add a scope like so:
if x then {
print(`this is a new scope`)
} else {
print(`another scope`)
}
end
While Loops
Loops are statements and do not evaluate to any value. While Loops can be written both with condition at the top and at the bottom. Like conditinal branches they do not introduce a new scope.
x = 0
do
x += 1
while x < 10
while x < 20 do
x += 1
end
Loop Control
To break out of a loop use the break
keyword and to skip to the next
iteration use continue
:
x = 0
while x < 20 do
print(x)
if x > 5 then break end
x += 1
end
In case there are nested loops you can specify which loop to continue or break out of by adding the loop index to the break or continue statements.
x = 0
while true do #! index 2
while true do #! index 1
while true do #! index 0
x += 1
if x > 4 then break 2 end
end
end
end
print(x) #! prints 5
The index for the current loop is 0 and increments towards the most outward loop.
For Loops
A for loop is a syntactic sugar that ultimately gets reduced to a while loop by the compiler. Unlike while loops, for loops introduce a lexical scope which is also reflected in the syntax. There are multiple ways the compile will reduce a for loop which are enumerated below:
Notice the induction variable in all examples below is defined within the lexical scope.
-
Use with containers
The above for loop will get roughly translated to:l = [1,2,3,4,5] for x in l { print(x) }
{ iter auto = l.get_iterator() while iter.has_next() do try x auto = iter.next() print(x) catch e IteratorStopException then break end end }
-
Use with an
IndexRange
for x in 1..5 { print(x) }
Just like a container IndexRange can be iterated over. However the compiler might perform additional optimizations as to what the loop above will ultimately get reduced to so it may not call the get_iterator of IndexRange at all. For example the above might get translated to:
{ x auto = 1 while x < 5 do print(x) x += 1 end }
-
Unrolling at compile time
#unroll for x in 1..5 { print(x) }
Unrolling is useful in situations where performance is critical and a jump instruction cannot be afforded or in situations where the loop induction variable (e.g.
x
above) needs to have a compile-time value. Unroll can only be used with IndexRanges that have compile time evaluation. The above IndexRange1..5
can be evaluated at compile time obviously and the entire loop will be translated to:{ print(x) print(x) print(x) print(x) }
Variable Type
You can explicitly set the loop variable type like so
l = [1,2,3,4,5]
for x Int32 in l {
print(x)
}
The elements in l
above will get assigned to Int32
. You may also take the
list elements by reference to modify them in place if the iterator actually
returns them by reference:
l = [1,2,3,4,5] List[Int64]
for x @auto in l { x *= 2 }
print(l) #! prints [2, 4, 6, 8, 10]
For iterators that return multiple values you can define multiple variables or skip over some of them:
dict = [`name`: `John`, `age`: 30, `height`: 6.4]
for k, v in dict {
print(`key: ` k `, value: ` v)
}
output:
key: name, value: John
key: age, value: 30
key: height, value: 6.40000000000000035527
To skip the value:
for k, in dict {
print(k)
}
output:
name
age
height
Zip multiple iterables
Using for loops you can iterate over multiple collections or iterables. Whichever iterator is exhausted first determines the final length of the loop:
keys = [`name`, `age`, `height`]
values = [`james`, 13, 33, 5784, `hi`, 3.3, NaN]
for k, v in keys, values {
print(`key: ` k `, value: ` v)
}
output:
key: name, value: james
key: age, value: 13
key: height, value: 33
This could be useful if you want to enumerate over the elements of a list or dictionary as in this example:
dict = [`name`: `John`, `age`: 30, `height`: 6.4]
for k, v, i in dict, ..dict.len() {
print(`` i ` - key: ` k `, value: ` v)
}
output
0 - key: name, value: John
1 - key: age, value: 30
2 - key: height, value: 6.40000000000000035527