Raku Programming/Control Structures
Flow Control
editWe've seen in earlier chapters how to create variables and use them to perform basic arithmetic and other operations. Now we're going to introduce the idea of flow control, using special constructs for branching and looping.
Perl shared many common flow control construct names with other languages such as C, C++, and Java. Raku has chosen to rename some of these common constructs. We'll post notes near the particularly confusing ones |
Blocks
editBlocks are chunks of code inside { }
curly brackets. These are set apart from the rest of the nearby code in a variety of ways. They also define a new scope for variables: Variables defined with my
and used inside a block are not visible or usable outside the block. This enables code to be compartmentalized, to ensure that variables only intended for temporary use are only used temporarily.
Branching
editBranching can occur using one of two statements: an if and an unless. if
may optionally have an else clause as well. An if
statement evaluates a given condition and if it's a true statement, the block following the if
is executed. When the statement is false, the else
block, if any, is executed instead. The unless
statement does the opposite. It evaluates a condition and only executes its block when the condition is false. You cannot use an else
clause when using unless
.
Relational Operators
editThere are a variety of relational operators that can be used to determine a truth value. Here are some:
$x == $y; # $x and $y are equal
$x > $y; # $x is greater than $y
$x >= $y; # $x is greater than or equal to $y
$x < $y; # $x is less than $y
$x <= $y; # $x is less than or equal to $y
$x != $y; # $x is not equal to $y
All of these operators return a boolean value, and can be assigned to a variable:
$x = (5 > 3); # $x is True
$y = (5 == 3); # $y is False
The parentheses above are used only for clarity; they are not actually necessary.
if
/unless
edit
Let's start off with an example:
my Int $x = 5;
if ($x > 3) {
say '$x is greater than 3'; # This prints
}
else {
say '$x is not greater than 3'; # This doesn't
}
Notice in this example above that there is a space between the if
and the ($x > 3)
. This is important and is not optional. The parsing rules for Raku are clear on this point: Any word followed by a (
opening parenthesis is treated as a subroutine call. The space differentiates this statement from a subroutine call and lets the parser know that this is a conditional:
if($x > 5) { # Calls subroutine "if"
}
if ($x > 5) { # An if conditional
}
To avoid all confusion, the parenthesis can be safely omitted:
if $x > 5 { # Always a condition
}
unless
edit
unless
has the opposite behavior of if
:
my Int $x = 5;
unless $x > 3 {
say '$x is not greater than 3'; # This doesn't print
}
No else
clause is allowed after unless
.
Postfix Syntax
editif
and unless
aren't just useful for marking blocks to be conditionally executed. They can also be applied in a natural way to the end of a statement to only affect that one statement:
$x = 5 if $y == 3;
$z++ unless $x + $y > 8;
These two lines of code above only execute if their conditions are satisfied properly. The first sets $x
to 5 if $y
is equal to 3. The second increments $z
unless the sum of $x + $y
is greater than 8.
Smart Matching
editSometimes you want to check if two things match. The relational operator ==
checks if two values are equal, but that's very limited. What if we wanted to check other equality relationships? What we want is an operator that just does what we mean, no matter what that might be. This magical operator is the smart match operator ~~
.
Now, when you see the ~~
operator, you probably immediately think about strings. The smart match operator does a lot with strings, but isn't restricted to them.
Here are some examples of the smart match operator in action:
5 ~~ "5"; # true, same numerical value
["a", "b"] ~~ *, "a", *; # true, "a" contained in the array
("a" => 1, "b" => 2) ~~ *, "b", *; # true, hash contains a "b" key
"c" ~~ /c/; # true, "c" matches the regex /c/
3 ~~ Int # true, 3 is an Int
As you can see, the smart match operator can be used in a variety of ways to test two things to see if they match in some way. Above we saw an example of a regular expression, which we will discuss in more detail in later chapters. This also isn't a comprehensive list of things that can be matched, we will see more things throughout the book.
Given / When
editRaku has a facility for matching a quantity against a number of different alternatives. This structure is the given
and when
blocks.
given $x {
when Bool { say '$x is the boolean quantity ' ~ $x; }
when Int { when 5 { say '$x is the number 5'; } }
when "abc" { say '$x is the string "abc"'; }
}
Each when
is a smart match. The code above is equivalent to this:
if $x ~~ 5 {
say '$x is the number 5';
}
elsif $x ~~ "abc" {
say '$x is the string "abc"';
}
elsif $x ~~ Bool {
say '$x is the boolean quantity ' ~$x;
}
The given
/when
structure is more concise than the if
/else
, and internally it might be implemented in a more optimized way.
Loops
editLoops are ways to repeat certain groups of statements more than once. Raku has a number of available types of loops that can be used, each of which has different purposes.
for
loops
edit
for blocks take an array or range argument, and iterate over every element. In the most basic case, for
assigns each successive value to the default variable $_
. Alternatively, a specific variable can be listed to receive the value. Here are several examples of for
blocks:
# Prints the numbers "12345"
for 1..5 { # Assign each value to $_
.print; # print $_;
}
# Same thing, but using an array
my @nums = 1..5;
for @nums {
.print;
}
# Same, but uses an array that's not a range
my @nums = (1, 2, 3, 4, 5);
for @nums {
.print;
}
# Using a different variable than $_
for 1..5 -> $var {
print $var;
}
In all the examples above, the array argument to for
can optionally be enclosed in parenthesis too. The special "pointy" syntax ->
will be explained in more detail later, although it's worth noting here that we can extend it to read multiple values from the array at each loop iteration:
my @nums = 0..5;
for @nums -> $even, $odd {
say "Even: $even Odd: $odd";
}
This prints the following lines:
Even: 0 Odd: 1 Even: 2 Odd: 3 Even: 4 Odd: 5
for
can also be used as a statement postfix, like we saw with if
and unless
, although with some caveats:
print $_ for (1..5); # Prints "12345"
print for (1..5); # Parse Error! Print requires an argument
.print for 1..5; # Prints "12345"
loop
edit
C programmers will recognize the behavior of the loop
construct, which is the same format and behavior as the for
loop in C. Raku has reused the name for
for the array looping construct that we saw in the previous section, and uses the name loop
to describe the incremental behavior of C's loops. Here is the loop
structure:
loop (my $i = 0; $i <= 5; $i++) {
print $i; # "12345"
}
In general, loop
takes these three components:
loop ( INITIALIZER ; CONDITION ; INCREMENTER )
The INITIALIZER
in a loop
is a line of code that executes before the loop begins, but has the same lexical scope as the loop body. The CONDITION
is a boolean test that's checked before every iteration. If the test is false, the loop exits, if it is true, the loop repeats. The INCREMENTER
is a statement that happens at the end of the loop, before the next iteration begins. All of these parts may be optionally omitted. Here are five ways to write the same loop:
loop (my $i = 0; $i <= 5; $i++) {
print $i; # "12345"
}
my $i = 0; # Small Difference: $i is scoped differently
loop ( ; $i <= 5; $i++) {
print $i;
}
loop (my $i = 0; $i <= 5; ) {
print $i; # "12345"
$i++;
}
loop (my $i = 0; ; $i++) {
last unless ($i <= 5);
print $i; # "12345"
}
my $i = 0;
loop ( ; ; ) {
last unless ($i <= 5);
print $i; # "12345"
$i++;
}
If you want an infinite loop, you can also omit the parentheses instead of using (;;):
my $i = 0;
loop { # Possibly infinite loop
last unless ($i <= 5);
print $i; # "12345"
$i++;
}
repeat
blocks
edit
A repeat block will execute its body at least once as the condition follows after the block.
In the example below you can see that even though $i
is larger than two, the block
will still run.
my $i = 3;
repeat {
say $i;
} while $i < 2;