Lesson 06 – Functions

Functions are used when you want to reuse a block of code. You call or invoke a function by its name and passing arguments if the functions accepts any.

In Perl, @_ contains a list of arguments that was passed to the function. This list will have scalars, arrays and hashes flattened into one.

The example below will show how to call a function and define a function. Note that only a scalar was passed to the function and $_[0] contains that value;

#!/usr/bin/perl

use strict;
use warnings;

my $price = 12.99;

calculate_total($price);

sub calculate_total
{
    my $p = $_[0];

    print "You must pay ", $p;
}

When you run the following script, Perl will give you a warning that says “Odd number of elements in hash assignment” and $d is undef. This is because the hash is greedy and gets all the values from @_.

#!/usr/bin/perl

use strict;
use warnings;

my %pizza = (plain => 9.99, pepperoni => 10.99);
my $drink = 3.99;

display_price(%pizza, $drink);

sub display_price
{
    my (%p, $d) = @_;

    foreach my $i (keys %p)
    {
        print $p{$i}, "\n";
    }
    print $d, "\n";
}

Instead, you will want to pass the scalar first and then the hash. This will produce what you expected.

#!/usr/bin/perl

use strict;
use warnings;

my %pizza = (plain => 9.99, pepperoni => 10.99);
my $drink = 3.99;

display_price($drink, %pizza);

sub display_price
{
    my ($d, %p) = @_;

    foreach my $i (keys %p)
    {
        print $p{$i}, "\n";
    }
    print $d, "\n";
}

This example demonstrates how hash references are passed to functions.

#!/usr/bin/perl

use strict;
use warnings;

my %pizza = ( plain => { medium => 7.99, large => 9.99}, 
              pepperoni => { medium => 9.99, large => 10.99}, 
              supreme => { medium => 10.99, large => 12.99}, 
              veggie => { medium => 9.99, large => 10.99}
            );
my %order = ( plain => { large => 2},
              pepperoni => { large => 2}
            );

calculate_total(\%pizza, \%order);

sub calculate_total
{
    my ($p, $o) = @_;

    my $total = 0;
    foreach my $style (keys %$o) {
        print "$style \n";
        foreach my $size (keys %{ $o->{$style} }) {
            my $pies = $o->{$style}{$size};
            print "$size: $pies pies\n";

            $total += ($p->{$style}{$size} * $pies);
        }
    }
    print "Your total is ", $total, "\n";
}

If you want to return $total to the main, you will have to use a return statement inside the function.

return $total;

Lesson 05 – References

In Perl, there is no such things as array of arrays, array of hashes, hash of arrays and hash of hashes. Perl has no values that are arrays or hashes.

$president{'name'} = ('Clinton', 'Bush', 'Obama');

This is a scalar assignment (because you’re assigning to a scalar variable). That means the right-hand side will be evaluated in scalar context.

in scalar context, the comma operator evaluates its left operand, throws the result away, then returns the right operand.

$x = (1, 2);

In this line, 1, 2 will evaluate 1, ignore it, then return 2 and you’ll get a warning for having a constant in void context.

“You can’t have a hash whose values are arrays; hash values can only be scalars.” – from perlreftut

In Perl, “value” is synonymous with “scalar value”. Perl is somewhat unique with its containers/values duality. You have containers like scalar variables, arrays, and hashes and then you have values like undef, numbers, strings, references.

The main things to keep in mind are the behaviors of arrays, hashes, and the comma operator in scalar/list context. In list context it evaluates both of its operands in list context, then concatenates them. Or shorter: , is list concatenation. An array in scalar context yields the number of its elements. An array in list context yields a list of its elements. A hash in scalar context yields a string (this is somewhat obscure). the main thing to keep in mind is that for an empty hash that string will be “” (false), and for a non-empty hash it will be true.

In Perl, when we use an array with a reference to another array, that reference is considered a scalar.

In Perl, an array is a container and a list is … multiple values.
The following example uses references to build a nested data structure. It is an anonymous hash inside a hash. Notice how it uses curly braces for the anonymous hash instead of parentheses. (Square brackets would be used if it was an anonymous array.)
#!/usr/bin/perl

use strict;
use warnings;

my %pizza = (
              "plain" => {"medium" => 7.99, "large" => 9.99}, 
              "pepperoni" => {"medium" => 9.99, "large" => 10.99}, 
              "supreme" => {"medium" => 10.99, "large" => 12.99}, 
              "veggie" => {"medium" => 9.99, "large" => 10.99}
            );

print "A large pepperoni pizza costs $", $pizza{pepperoni}{large}, "\n";

Of course, you can build the same nested data structure by explicitly assigning a hashref to a scalar variable and using it inside a hash.

my %plain_pricing = ("medium" => 7.99, "large" => 9.99);
my $plain = \%plain_pricing;
my %pepperoni_pricing = ("medium" => 9.99, "large" => 10.99);
my $pepperoni = \%pepperoni_pricing;
my %supreme_pricing = ("medium" => 10.99, "large" => 12.99);
my $supreme = \%supreme_pricing;
my %veggie_pricing = ("medium" => 9.99, "large" => 10.99);
my $veggie = \%veggie_pricing;
my %pizza2 = (
               "plain" => $plain, 
               "pepperoni" => $pepperoni, 
               "supreme" => $supreme, 
               "veggie" => $veggie
             );

print "A large pepperoni pizza costs $", $pizza2{pepperoni}{large}, "\n";

You can also assign a value directly to the nested data structure and Perl will automatically create the reference.

my %pizza3;

$pizza3{pepperoni}{large} = 10.99;
print "A large pepperoni pizza costs $", $pizza3{pepperoni}{large}, "\n";

If you want to explicitly destroy a reference, you write undef $ref;

Normally there is no need to manually clear references. They get destroyed at the end of whatever scope they are used in.

Think of it as “once the variable is no longer needed, Perl undef()s it for you.”

Lesson 04 – Regular Expressions

Regular expressions describe text patterns.

Each text pattern in a regular expression is called a metacharacter.

=~ is the operator used for regular expressions.

When characters are written between [ and ] it means they are part of a character class. One character from the character class must match in order  to continue evaluating the rest of the regular expression.

Inside a character class, – indicates a range and ^ indicates negation.

Perl has shortcuts for the most common character classes.

[a-zA-Z0-9_] can be written as \w and [^a-zA-Z0-9_] as \W.

Metacharacters.

  • . means match any character except a newline
  • \w means match any alphanumeric character or the underscore
  • \W means match any character that is not alphanumeric or the underscore
  • \d means match any character that is a digit
  • \D means match any character that is not a digit
  • \s means match any character that is a whitespace such as a space, newline or a tab
  • \S means match any character that is not a whitespace
  • ^ means match the beginning of the line
  • $ means match the end of the line

^ and $ are called anchor metacharacters. They’re also sometimes called assertions.

Quantifiers describe how many times a character can be found in a string.

  • * means zero or more
  • + means one or more
  • ? means zero or one time
  • {n} means n times where n is an integer
  • {n,m}means any number of times between n and m
  • {n,} means n or more times

Modifiers.

  • i (Ignore case)
  • s (Single line)
  • u (Unicode)
  • m (Multiline)
  • x (Verbose)
  • l (Locale)

m/regular expression here/ is the same as /regular expression here/. It checks whether the first operand matches the text pattern.

s/find this regular expression/replace with this text/

Regex can be used to find a certain text and substitute it with another text.

The following example substitutes spaghetti with pizza:

#!/usr/bin/perl

use strict;
use warnings;

my $sentence = "I love eating spaghetti.";

$sentence =~ s/spaghetti/pizza/;

print $sentence, "\n";

This example substitutes the number of slices to 4:

my $order = "3 slices of plain pizza
5 slices of pepperoni pizza";

$order =~ s/\d+/4/g;
print "Your order has been changed to:\n", $order, "\n";

/g modifier means match the regex globally so it replaces all occurrences of a digit to 4.

The program prints this on the screen:

Your order has been changed to:
4 slices of plain pizza
4 slices of pepperoni pizza

When you want to take a portion of a string based on your regular expression, you must put parentheses around each pattern that you want to match. First matching part will be stored in $1, second matching part will be stored in $2, etc. We call this process capturing.

If you read perlrequick, there is this example:

($hours, $minutes, $second) = ($time =~ /(\d\d):(\d\d):(\d\d)/);

It’s capturing this:

($time =~ /(\d\d):(\d\d):(\d\d)/) # returns $1, $2, $3

The values are assigned to ($hours, $minutes, $second)
You need the parentheses to group the expression like this. Otherwise it’d first assign $time to $hours, then check $second (undef) against the regex. (Precedence issue with = and =~)

Notes: In Programming Perl, it says that an easy mistake is to think that \w matches a word. Use \w+ to match a word.

When you’re learning how to make regex, I found this very useful. http://gskinner.com/RegExr/

Lesson 03 – Loop

Looping is useful when you want to execute a block of code several times.

In Perl, there are four styles of loops.

#!/usr/bin/perl

use strict;
use warnings;

my @pizza = ("plain", "pepperoni", "supreme", "veggie");

foreach (@pizza)
{
    print $_, "\n";
}

When working with arrays, foreach is usually preferred. In the above code, it loops until you reach the end of the array.

In Perl, foreach can be written for.

for (@pizza)
{
    print $_, "\n";
}

$_ is a special kind of variable (the topic variable) that will get an element from the array on each iteration. In the Modern Perl book, the author describes the usage of $_ as similar to the word “it” in the English language.

for (my $i=0; $i<@pizza; $i++)
{
    print $pizza[$i], "\n";
}

The C-style for loop does the same thing but it requires a conditional so that it continues to loop and increment $i by 1 until $i is greater than or equal to the size of the array.

The Perl-style for loop iterating with index over an array is this:

for my $m (0..$#pizza) 
{
     print $pizza[$m], "\n";
}

.. is Perl’s range operator and $#pizza is the highest index.

my $k = 0;

while ($k<@pizza)
{
    print $pizza[$k], "\n";
    $k++;
}

This is the while loop that does exactly the same thing. It loops as long as $k is less than the size of the array.

It will exit the loop when the condition is false.

The until loop is the opposite of the while loop. It will loop and exit when the condition is true.

my $j = 0;

until ($j>=@pizza)
{
    print $pizza[$j], "\n";
    $j++;
}

Notice how @pizza is used instead of scalar(@pizza). That’s because scalar context is implicit there.

Lesson 02 – Conditional

If you want to do something based on a certain criteria, you should use a conditional.

#!/usr/bin/perl

use strict;
use warnings;

my $pizza = "pepperoni";

if ($pizza eq "pepperoni")
{
     print "Yes, my pizza is pepperoni!\n";
}
elsif ($pizza eq "plain")
{
     print "Plain pizza is good too.\n";
}
else
{
     print "I don't want it.\n";
}

The eq operator checks if the two strings match.

If you are dealing with numbers, you should be using one of these numeric comparison operators:

  • == checks whether two numbers are equal
  • > checks whether the first number is greater than the second number
  • >= checks whether the first number is greater than or equal to the second number
  • < checks whether the first number is less than the second number
  • <= checks whether the first number is less than or equal to the second number
  • != checks whether the first number is not equal to the second number
  • <=> is the sort comparison operator 1 is returned if the first number is greater than the second number. 0 is returned if the first number is equal to the second number. -1 is returned if the first number is less than the second number.

When using simple expressions, you can use the postfix form (also called the statement modifier) which looks something like this:

print "I like pepperoni pizza." if $pizza eq "pepperoni";

Notice how it doesn’t require any curly braces for the postfix form.

unless (defined($pizza))
{
    print "Tell me what kind of pizza you want.\n";
}

The conditional means the same thing as “if $pizza is not defined”.

if (!defined($pizza))
{
    print "Tell me what kind of pizza you want.\n";
}

The ternary conditional operator is the same as using if and else. It looks like this:

$pizza eq "pepperoni" ? print "I like pepperoni pizza." : print "I don\'t want anything else.";

given/when is equivalent to an if/elsif chain

given ($pizza)
{
when "pepperoni" { print "Yes, my pizza is pepperoni!\n"; }
when "plain" { print "Plain pizza is good too.\n"; }
default { print "I don't want it.\n"; }
}

If you’re familiar with other programming languages like C, given/when is basically switch/case spelled differently.

Notes: expression of ‘given’ is in scalar context, and always assigns to $_ (topic variable).

$_ is available in that block of code. It has lexical scope.

‘when’ is actually not required in a ‘given’ block. It’s simply a way of quickly testing a value by assigning it to $_

given/when does an implicit smart match

given (whatever()) { when (10) { ... } }

when (10) is implicitly when ($_ ~~ 10)

Lesson 01 – Variables

A variable is used by a program to store a value. In Perl, there are three different types of variables.

  • Scalar – one value of either a number, a string, a dualvar, a reference or undef
  • Array – a set of values of indexed values starting with position 0
  • Hash – a set of key-value pairs

Scalars use $ in front of the variable name. Array use @ in front of the variable name. Hashes use % in front of the variable name.

#!/usr/bin/perl

use strict;
use warnings;

my $pizza = "plain";
my @pizza = ("plain", "pepperoni", "supreme", "veggie");
my %pizza = ("plain" => 5, "pepperoni" => 7, "supreme" => 6, "veggie" => 4);

print "The pizza I ordered is ", $pizza, "\n";
print "The second pizza on the menu is ", $pizza[1], "\n";
print "The restaurant has ", scalar(@pizza), " kinds of pizza\n";
print "The restaurant has ", scalar(keys(%pizza)), " kinds of pizza\n";
print "The restaurant has ", $pizza{"veggie"}, " of the veggie pizzas\n";

When you run this program, it will print this on the screen:

The pizza I ordered is plain
The second pizza on the menu is pepperoni
The restaurant has 4 kinds of pizza
The restaurant has 4 kinds of pizza
The restaurant has 4 of the veggie pizzas

Like in many other programming languages, arrays start at position 0 for the first element, position 1 for the second element, position 2 for the the third element, etc.

The function scalar() counts the total number of elements of a list.

The function keys() grabs the keys of a hash and puts it in a list.

When you want to grab a value from a hash, you put the key value in between curly braces.

It’s important to understand the context of which you are using variables. Is the variable in scalar context or list context?