Showing posts with label perl. Show all posts
Showing posts with label perl. Show all posts

A double for loop

I think it could be useful, at least for myself, to keep track of the piece of code I have written today in Perl for future reference. Nothing too advanced, but with a few interesting point touched in its few lines.

It is a double for loop scanning all the argument passed to the script and storing them in an array, ready to be used in the rest of the script itself.

Let's have a look at the code:
ARG_LOOP: # 1
foreach(@ARGV) # 2
{
for(my $i = 0; $i < 3; $i++) # 3
{
if(/(.*_$TAGS[$i])$IN_EXT$/) # 4
{
if(defined $files[$i]) # 5
{
print "You can't pass more than one $TAGS[$i] file to the script\n";
print $help;
exit 1;
}

$files[$i] = $1; # 6
next ARG_LOOP; # 7
}
}

print "Unexpected argument detected\n"; # 8
print $help;
exit 2;
}

1. A label is declared so that a "next" instruction could be used in the inner loop to skip to the next iteration for the outer loop.
2. Looping on all the element in the ARGV array, containing the arguments passed to the script. The current item is stored in the default $_ variable.
3. Internal loop: we are expecting three input parameter.
4. Each input parameter should respect a specific pattern: first part is seen as a block containing free text, then an underscore followed by a previously defined tag. The final part of the parameter is a previously defined INput EXTension. Notice the $ before the final slash: nothing is expected after the extension.
5. There should be exactly one input parameter for each defined tag. So we check if the current file has been already defined in a previous iteration. If this is the case, we print a message to the user (in the $help variable is showed how the script should be called) and return to the system.
6. A match has been found, and the file has not already be defined for the current tag. The (first and unique) block defined in the pattern is assigned to the current file element.
7. We stop the internal loop and give back the control to the next iteration of the external loop.
8. We shouldn't normally get here, since all expected input parameter should match all the stored tags. If we find a parameter not matching any tag, we print some helpful comment and return to the system.

Go to the full post

Checking user input

We are writing a Perl script, and we have to choose a execution path accordingly to the user decision. The user should answer y or n, both lower and uppercase, to our question. Any other answer has to be rejected.

Here is a possible solution to this common requirement.

The code that requires the user decision is something like that:
print "First step completed\n";
if(stop_execution()) # 1
{
print "Correct data and retry.\n";
exit 1; # 2
}
else
{
print "Ready for the next step.\n";
}

print "This is the second step.\n";

# ...

exit 0;

1. We are about to describe our decision-making function stop_execution() just below. Here it suffices noticing that it would return a "true" value in case we actually want to interrupt the execution stream.
2. As usually expected, a script returns a non-zero value in case of unhappy ending.

Our user interaction consists in asking if he is ready for the next step, iterating until his answer is a valid one, and returning to the user a true/false value:
sub stop_execution
{
my $prompt = "Do you want to continue? [y/n]: "; # 1

print $prompt;
while(<STDIN>) # 2
{
if(/^n$/i) # 3
{
return 1;
}
elsif(/^y$/i) # 4
{
return 0;
}
print $prompt; # 5
}
}

1. This is the string that will be used as prompt to the user to get an input.
2. We read from the standard input stream, and put the result in the default $_ variable.
3. If $_ is matching to the specified pattern, we return a non-zero value (that means, true). The pattern is just a letter, n, and we specify that the string should match to it from the beginning to its end. Besides, the 'i' character after the closing slash tells that the match is case-insensitive. So both "n" and "N" are accepted.
4. Same as above, but the pattern is matching against "y" (and "Y") and we return 0 (meaning false).
5. If the user input does not match with our expectations, we repeat the question and iterate.

Go to the full post

Dump that table

In a previous post we have seen how to connect to a database using the Perl DBI module, here we write a subroutine, to be called after the connection has been estabilished and before disconnecting, to actually perform a query.

Assuming we have a table named t in the current database schema, here is it:

sub dumpThatTable
{
my $dbh = shift;
my $sth = $dbh->prepare("select * from t");
$sth->execute();
if($sth->err())
{
print "An error occurred: ".$sth->errstr()."\n";
return;
}

my @row;
print "@row\n" while(@row = $sth->fetchrow_array());
print "An error occurred: ".$sth->errstr()."\n" if $sth->err();
}

The subroutine expects to be called with a valid database handle as a parameter, that we extract and put in the local variable $dbh. Then we prepare a SQL statement, putting the result in another local variable, $sth. We try to execute the statement and, in case of success, we loop calling fetchrow_array() on the statement to get the next row and simply printing it.

Go to the full post

Perl chop

Perl chop() shouldn't be easily confused with a pork chop, but there could be a mixup with another, more popular, Perl function: chomp(). The latter is used to remove the string terminator (usually a backslash-n) from the string itself, while chop() is less selective, and removes the last character from a string, whatever it is.

We usually pass a scalar to the chop() function, and usually it is a string. The function returns the last string character (or ASCII NUL, the backslash-zero character, if there is nothing to chop in the string) and it has the side effect of removing that last character from the string itself.

Here is a short example:

my $countdown = "0123456789";
while(1)
{
my $current = chop($countdown);
last if(ord($current) == 0);

print "$current\n";
}

In an infinite loop we iteratively chop a string ouputting the chopped character. If the returned value from chop() is the NUL character we terminate the loop, otherwise we print it on a dedicated line.

Go to the full post

Hello DBI

The most commonly used Perl module to access database is DBI. Once installed this Perl module on my current machine (I simply opened a cpan shell and I entered the command "install DBI" and I let cpan doing its dirty job - obviously an active connection to the internet is required). I wrote a silly little perl script to test everything works fine.

This code just open a connection to a mysql database, and then it close it:

#!/usr/bin/perl
use strict;
use warnings;
use DBI;

my $dbh = DBI->connect("dbi:mysql:test", "root", "password") ||
die "Connection error: $DBI::errstr\n";

$dbh->disconnect();
print "Done.";

There is something interesting even in these few lines.

First of all we see that to start a connection we call the connect() function in the DBI package passing three paramenters representing the database to which we are about to connect (protocol and database name), the database user and its password.

If connect() fails, we let our script die, showing the error message that is stored in DBI::errstr.

Finally (!) we close the connection calling the disconnect() method on the dbh object resulting by the connect() call.

Go to the full post

Set of characters

Another useful variation on basic Perl regular expressions is the one represented by the usage of set of characters.

If you can specify your pattern as a bunch of letters where some are fixed and other are varying, we could put the varying ones in a set delimited by square brackets.

Say that we want to check if our string has in it on of these three words: dark, dirk, dork. One way of doing it is considering that they are almost the same. Actually, three letters are exactely the same, and one, the second, is a choice among three different ones.

We can formalize it in this way:

$pattern = "d[aio]rk";
if(/$pattern/) {
print "found!\n";
}

In a set of choices the caret (^) metacharacter (already seen as starting anchor) assumes the sense of a negator.

Our search now is about a pattern starting with "d", ending by "rk", and with just a character in the middle that could be everything "i" or "o". So we won't accept "dirk" or "dork", but we will be cool with "dark" or even "durk":

$pattern = "d[^io]rk";

if(/$pattern/) {
print "found!\n";
}

In a set of character we can specify a range. For instance, if we are looking again for our d.rk word, but now we relax the requisites, letting go anything as second letter that would be a lowecase alphabetical, we could write the check in this way:

$pattern = "d[a-z]rk";
if(/$pattern/) {
print "- found!\n";
}

The second letter now could be anything ranging from a to z (lowercase).

For common choices Perl makes available shortcuts: \d is expanded to [0-9]; \w to [0-9A-Za-z_]; \s to [ \t\n\r]. The uppercase version is a negation. So, for instance, \D means anything but a digit.

Chapter 5 of Beginning Perl by Simon Cozens focuses on regular expressions.

Go to the full post

Anchors

When basic Perl regular expressions are not enough, we improve them using a number of different optional features.

Anchors are useful when we have specific requirements on the position of the pattern in the string. A caret (^) says that we want the pattern being at the beginning of the string, a dollar ($) is for the end.

If we want to check a string for having or not a full stop at its end, we could write this Perl code:

$pattern = "\.";
if(/$pattern$/) {
print "Full stop terminated string\n";
}

Notice that we have to quote the dot (.) because it is a metacharacter, meaning "whatever character but newline".

To check if our string begins with a specific pattern we write something like this:

$pattern = "It";
if(/^$pattern/) {
print "It starts the string\n";
}

Chapter 5 of Beginning Perl by Simon Cozens focuses on regular expressions.

Go to the full post

Simple pattern matching

Perl is strong at text management, and regular expressions are a substantial part of its strenght. Here we start seeing some basic pattern matching.

Given a string:
my $text = "It was a dark and stormy night.";
A common task is checking if a substring is included in it.

The simplest version of it is the exact matching. To check if the substring "dark" is included in the original string, we can write:

if($text =~ /dark/) {
print "Yes, it's dark\n";
}

The matching operator is tilde (~), prefixed with an equal to check for actual matching, with a exclamation mark for the failure of the test, as shown here:

if($text !~ /light/) {
print "No, it's not light\n";
}

As usual in Perl, there is a way of avoiding some typing. If we put the string object of our search in the default scalar:
$_ = $text;
We could rewrite the pattern checking without explicitely refer to the original string. Besides, we could make our checking easier to change using a variable instead of a constant string:

my $pattern = "dark";
if(/$pattern/) {
print "Found it\n";
}

A usual requirement in pattern matching is relaxing it just a bit, to check the pattern in a case insensitive way.

The idea is that we usually want to know to have an OK when we check for "it" and we have "It" (or "IT", and even "iT"). The standard check it is too strict, in this case:

$pattern = "it";
if(not /$pattern/) {
print "Can't find it\n";
}

But we can easily overcome this issue using the "i" modifier:

if(/$pattern/i) {
print "Found it\n";
}

Chapter 5 of Beginning Perl by Simon Cozens focuses on regular expressions.

Go to the full post

Getting out of nested loops

Nested loops make code less readable, so it is usually better avoid them in the first place. On the other side, sometimes they are the most natural solution to a problem, so not using them make the code less clear.

One problem that we could face when we write nested loop is that is not easy to completely get out from it when we are in an internal loop. There are to classical ways to achieve this result: using flag variables, that make the code clumsy; using goto instructions, that make the code a mess.

Perl offer a third way, using a label loop - that actually let us use a sort of domesticated goto mechanism.

The problem that we want to solve is this: we want to loop indefinitely on the input provided by the user, stopping only when one of a list of magic words is provided.

The natural way of implementing a solution is having a loop on STDIN, and then an internal loop for checking the user input against the list of terminators.

The issue is: what should be do when we find that the user input a terminator? We should get out of the external loop, obviously, but how we could achieve it. We have a keyword, last, that is used to step out a loop, but it works only for the current loop.

The solution is marking with a label the external loop (traditionally labels are written all-upperacase) and specifying the loop we want to step out with the last statement:

my @terminator = qw(quit exit stop);

LOOP: while(<>) {
chomp;
for my $check (@terminator) {
last LOOP if $check eq $_;
}
print "---> $_\n";
}
print "Done.\n";

If we used last without specifying the LOOP label, the result would have been disappointing. No way of getting out of the while, we would have been trapped forever (or till an interrupt occurred) in the loop.

Chapter 4 of Beginning Perl by Simon Cozens is about loops and decisions.

Go to the full post

Diamond operator

The Perl so called diamond operator extract a line from the passed file handle. If no file handle is specified, Perl checks the @ARGV array, if at least an element is there, it considers them as file names and tries to open them. If no argument has been passed, the diamond operator assumes standard input (STDIN).

Any time we call the diamond operator, we read a line from the associated file (assumed as a text file). If we are at its end, an undef value is returned. If we are working with a "real" file, a bunch of data stored in the file system, it is quite clear what this means. A bit fuzzier is in case of a stream, like what happens when we use standard input.

To signal that we consider close our stream from standard input to our perl application we use a ctrl-Z (Windows) or ctrl-D (UNIX).

Here is a while loop that run on the standard input till we signal we have got enough of it, and echo the line we entered:

while(defined($_ = <STDIN>)) {
print "---> $_";
}
print "Done\n";

We can rewrite this loop in this way:

while(<>) {
print "---> $_";
}

The latter is more flexible, since it normally works on STDIN, but we can use another file simply passing it as argument to the perl script on the command line.

Chapter 4 of Beginning Perl by Simon Cozens is about loops and decisions.

Go to the full post

Arguments

We can pass arguments from the system command line to Perl. These arguments are made available to us by an array named @ARGV.

If we want to terminate our perl script in case no argument is passed, we could write something like this:
die "You passed no parameter\n" if not @ARGV;
A couple of things to say on this line:

Firstly, I used the so called statement modifier so common in Perl programming. The if check is after the statement that has to be executed in case of success. It looks wierd the first time, but after a while it gets kind of logical.

Secondly, @ARGV is used in a scalar context, so what we are cheching there is its size. The logical "not" operator, equivalent to the exclamation mark "!", negates the value. So, we can read the line in this way: terminate the program printing that message if no argument has been passed.

It is a matter of taste, we could have written equivalently:
die "You passed no parameter\n" unless @ARGV;
Counting on the fact that "unless" is a Perl synonym for "if not".

Once we ensured the array of arguments is not empty, we can print its elements in this way:
print "You passed ".@ARGV." arguments: @ARGV\n";
We uses a first time the @ARGV array in a scalar context, so we get the number of its elements, and then in a interpreted string, so we get all its elements separated by a blank.

If we want to print its elements in a more decorated way, we could use this piece of code:

foreach(@ARGV) {
print "'$_' ";
}
print "\n";

In Perl "for" and "foreach" are synonym. Besides, could be interesting notice the usage of the single quotes inside a double quoted string.

Say that we are expecting numeric values as input arguments, and we want to sum them all and then show the result to the user. We could do that in this way:

my $res;

for (@ARGV) {
$res += $_;
}
print "Sum: $res\n";

But we could use again a statement modifier, this time based on "for":

$res += $_ for @ARGV;
print "Sum: $res\n";

Chapter 4 of Beginning Perl by Simon Cozens is about loops and decisions.

Go to the full post

Switch

Believe it or not, until version 5.8 Perl had no switch. And even now switch is not a core part of the language. How could perl programmers do without it? Emulation is the answer.

We ask a numeric value to the user, there are many possible branches we can take accordingly to the passed value, so the natural way of designing the code would be through a switch. In perl (pre 5.8) we could emulate it in this way:

print "Enter 1 or 2: ";
my $value = <>;
for($value) {
$_ == 1 && do { print "one\n"; last; };
$_ == 2 && do { print "two\n"; last; };
print "unexpected\n";
}

Notice the last statement in the do block. If we didn't write it, the "default" would have been executed by any branch.

From Perl version 5.8 we can use a real switch. For our simple problem this a possible solution:

use Switch;

switch($value) {
case 1 { print "one\n" }
case 2 { print "two\n" }
else { print "unexpected\n" }
}

Go to the full post

Getting input from keyboard

We have written a lot of stuff to the standard output, in the previous Perl examples, now it's time to start getting some input from the standard input.

Nothing easier than that. Here is an improved Hello program that even ask to the user for a name:

print "Input your name: ";
my $name = <STDIN>;
print "Hello, $name!\n";

Actually, this was a buggy example. The fact is that the line we got from standard input includes the newline at its end. We could solve this problem passing the string to the chomp() function, the check for newline at its end and trim them off:

chomp($name);
print "Hello, $name!\n";

But why should we put reading and trimming in two different lines? Won't it be clearer for the reader this?

print "Input your name: ";
chomp(my $name = <STDIN>);
print "Hello, $name!\n";

And if we really want to save some typing, why should we specify STDIN. We normally expect to read data from there so, as usual in Perl, default could be omitted:

print "Input your name: ";
chomp(my $name = <>);
print "Hello, $name!\n";

Go to the full post

Hash

Associative arrays are commonly known as hash in the perl community. If you don't know what I'm talking about in a case nor the other, think to them as unsorted collection of data pairs.

A pair has a first value, known as key (or hash key), that should be unique in the collection; and a second value, that has no special name (it is just called value) and no special constrain either.

We have seen that a perl scalar is identified by a $, an array by @, an hash has a % as a first character in its name.

There is a strong relation between array and hash. So strong, that it is easy create an hash from an array. Given this array:

my @months = ( "Jan", 31, "Feb", 28, "Mar", 31, "Apr", 30, "May", 31, "Jun", 30,
"Jul", 31, "Aug", 31, "Sep", 30, "Oct", 31, "Nov", 30, "Dec", 31
);
print "Months array: @months\n";

We create an hash from it simply by assignment:
my %months = @months;
But we should not expect that an hash would keep the elements in a specific order so, if we assign our hash to another array:

my @monthsA = %months;
print "Months again: @monthsA\n";

We have no guarantee that the elements in monthA would be in the same order that the ones in the month array.

We don't need to create an array as intermediate passage to an hash creation, we could directly initialize it as we would initialize an array:

my %monthsB = (
"Jan", 31,
"Feb", 28,
"Mar", 31,
"Apr", 30,
"May", 31,
"Jun", 30,
"Jul", 31,
"Aug", 31,
"Sep", 30,
"Oct", 31,
"Nov", 30,
"Dec", 31
);

There is an alternative notation, that uses the comma-arrow operator "=>" to make a bit clearer the statement, showing more explicitely the relation between key and value in the hash:

my %monthsC = ( Jan => 31, Feb => 28, Mar => 31, Apr => 30, May => 31, Jun => 30,
Jul => 31, Aug => 31, Sep => 30, Oct => 31, Nov => 30, Dec => 31
);

Since it is so common to have a string as key in an hash, the comma-arrow operator is designed to implicitly quote the characters on its left, converting them in a string.

Once we have an hash, we can get a value associated to a key using a notation close to the one for array:
print "Days in October: $months{Oct}\n";
The difference is that we have to use curly brackets and not square ones.

Instead of using a literal value (without qoutes), we can use a scalar:

my $month= "May";
print "Days in $month: $months{$month}\n";

Let's start again from an empty hash:
my %where;
We can add an element using the the same curly bracket notation we have used for reading an element:

$where{Eva} = "Turin";
print "Eva lives in $where{Eva}\n";

We should pay attention to the fact that key in an hash are unique so, if we use a key that is already in the hash, we don't add a new element, but change the value associated to the existing one:

$where{Eva} = "Berlin";
print "Eva lives in $where{Eva}\n";

Using an hash variable in a scalar context, gives us the number of element currently in the hash (and the total room currently available, more on this in a future post, I guess):
print %where."\n";
Notice that I didn't put the hash variable name in the string, because - try it yourself - that is not a smart move. Perl doesn't recognize it as an hash and print it as a literal string.

To get rid of an element, we use the delete operator:

delete $where{Eva};
print %where."\n";

After using it, we see that the number of element in the hash decreases. If we want to check directly if there is a specified key in an hash, we use the exists() function:

if(!exists $where{Eva}) {
print "No Eva here\n";
}


If we want

There are a couple of useful functions that help us working with hashes. With keys() we get an array containing all the keys in the passed hash, and values() does the same with the values, as someone would have correctly guessed:

my @kMonths = keys(%months);
print "@kMonths\n";

my @vMonths = values(%months);
print "@vMonths\n";

Actually, there are not many cases in which values() comes to help. On the other side, keys() could be very useful - here we see it at work for looping on all the elements of an hash:

for (keys %months) {
print "$_ has $months{$_} days\n";
}

Just do not expect to have the months printed in a specific order.

Chapter 3 of Beginning Perl by Simon Cozens is about arrays and associative arrays (hashes).

Go to the full post

Sort

Another commonly used array function in Perl is sort, that provides a way of ordering an array. By default the ordering algorithm is alphabetical ascending, but we can easily change it.

Say that we have a string array:
my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
But we are not happy with its current element disposition. Sometimes we could just need the reverse function, that we can use in this way
for(reverse(@months)) { print "$_\n"; }
But normally we require a more complex rearrangement. If we want to have our months array starting alphabetically with Apr and ending at Sep, we can call sort in its standard way:

my @sorted = sort @months;
print "@sorted\n";

If we want to do something more sophisticated, we can specify an explicit sort routine:

my @revSort = sort { $b cmp $a } @months;
print "@revSort\n";

The sort routine in included in braces and its value is negative, zero or positive accordingly to the relative order of the two parameter, $a and $b, passed to it by sort. In this case we wanted to order the array using an alphabetically descendent order, so we simply reverted the normally used comparison, $a cmp %b.

If you wonder what cmp is, it is a function that behaves just like the old C strcmp() function. I hope you don't need more to be said on the matter.

If we apply the standard sort algorithm to an array of integer, we get the (maybe unexpected) result of ordering it in ascending alphabetical order:

my @numbers = (4, 65, 1, 23, 7);
my @alphaSort = sort @numbers;
print "@alphaSort\n";

Why this happens should be quite clear, when we think better to it. Perl converts automatically numbers to strings, if it finds them in a string context. And by default sort apply the cmp function to decide how to order the elements in the array.

To get the array ordered as we probabily expected it from the beginning, we use the perl operator <=> that behaves like cmp but expecting numbers in input:

my @numSort = sort { $a <=> $b } @numbers;
print "@numSort\n";

Chapter 3 of Beginning Perl by Simon Cozens is about arrays and associative arrays (hashes).

Go to the full post

pop/push and shift/unshift

A Perl array is actually an implementation of the deque (double ended queue) concept. So Perl makes available to us two couples of functions (or operator - Perl looks a bit fuzzy on this distinction) on array: pop/push, to work on its tail; shift/unshift, for its head.

Let's create an array to do some testing:

my @array;
print "Empty array: @array\n";

I often find useful start doing a silly thing. For instance, what happens if I try to pop (that means: remove an item from the tail) an empty array?

my $item = pop @array;
print "Tried to pop but there was nothing in the array!\n" if(!defined $item);

We get no error, simply the result is set to undefined. Notice that I used the (funny) perl inverted syntax for "if", that is quite cool in a case like this.

Remember that for Perl the zero-lenght string, "", is considered equivalent to zero, that's why I had to check the result using the defined operator:

push @array, "";
$item = pop @array;
print "I've popped \"$item\" from the array\n" if(defined $item);

As a bonus, in the previous piece of code we have even seen how to use push to add an element at the end of an array. Actually, since that specific array was empty there was not much to see. Better if we push a couple of items, one after the other:

push @array, "one";
push @array, "two";
print "Array now is: @array\n";
$item = pop @array;
print "I've popped \"$item\" from the array\n";

The couple of functions shift/unshift work just the same, but at the beginning of the array:

unshift @array, "three";
print "Array now is: @array\n";
shift @array;
$item = shift @array;
print "I've shifted \"$item\" from the array\n";

Exactely as pop, also shift returns an undefined value when we try to act on an empty array:

$item = shift @array;
print "Tried to shift but there was no item in the array!\n" if(!defined $item);

Chapter 3 of Beginning Perl by Simon Cozens is about arrays and associative arrays (hashes).

Go to the full post

Dollar-pound

We know that if we use the name of an array variable in scalar context, it is intepreted as its size. But sometimes we are more interested in its last index value. A other-than-perl programmer would probabily just decrease by one the array length, but this is a really not perlish way of seeing it.

Perl provides an explicit operator, dollar-pound ($#), that returns the last item index in the referenced array. I'm ashamed but I should confess I'm not so into perl to see the actual beauty of such a construct.

Given an array, for instance our usual one:
my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
This way of looping on it is not considered very perlish:
for(0 .. @months -1) { print "$_: $months[$_]\n"; }
It's true that we have a reason not to use the handy for each loop - we should have used another loop variable to keep track of the current index - but that decrement on the array size is seen as lacking of beauty. It is usually considered nicer using instead the dollar-pound operator:
for(0 .. $#months) { print "$_: $months[$_]\n"; }

Chapter 3 of Beginning Perl by Simon Cozens is about arrays and associative arrays (hashes).

Go to the full post

For each loop

The for each loop is very handy when you want to do something on all the items in an array.

Here we have, again, this array:
my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
, we want to print it, but this time we want to show each month name on a different line.

As often happens in Perl, this is a one liner:
for my $month (@months) { print $month, "\n"; }
I declared month as a scalar variable local to the for each loop. Any round in the loop it gets a value from the array months, till the end of it is reached.

Actually, we could rewrite the loop in a even more succint way:
for (@months) { print $_, "\n" }
No loop variable is required, we can safely use the default scalar variable $_ instead, and we can even get rid of the last semicolon in the for each body.

Another example, where there is a change in the array we are working with. The problem here is that we want to double each value in an array:

my @values = ( 10, 20, 30, 40, 50 );
print "initial values: @values\n";
for(@values) { $_ *= 2 }
print "final values: @values\n";

Again a compact solution.

Chapter 3 of Beginning Perl by Simon Cozens is about arrays and associative arrays (hashes).

Go to the full post

Slicing arrays

Perl arrays are so flexible that someone (with that sort of twisted mind) could say it is quite fun to work with them.

Say that you have this definition of an array:
my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
And you want to print just some of its elements. Here is a very compact way to say "print the fourth, fifth element, and then all the elements from the eigth to nineth, each of them separated by a blank":
print "@months[3, 4, 7 .. 9]";
The array is in a double quoted string exactely to have it interpolated, that in this case means a blank inserted between each adiacent couple of elements.

Consider another array, and assume this one keep the sale numbers for the past year:
my @sales = (41, 32, 53, 34, 85, 36, 27, 98, 39, 60, 31, 42);
We can easily slice both arrays, months and sales, to show partial results:

print "Summer sales: ";
print "@months[5 .. 7] - @sales[5 .. 7]\n";

And we can use slices to manipulate values in an array. Here, for instance, we swap the values in two months:

print "Swapping Jun and Aug: ";
@sales[5, 7] = @sales[7, 5];
print "@months[5 .. 7] - @sales[5 .. 7]\n";

Chapter 3 of Beginning Perl by Simon Cozens is about arrays and associative arrays (hashes).

Go to the full post

Basic stuff on arrays

Perl array is not so low level as its C counterpart, it would make more sense to compare it with C++ std::vector, but it is not defined in a library, instead it is a part of the language itself.

We see at once if a perl variable is an array or a scalar, since the name of an array starts with an at sign (@), while we know that a perl scalar variable name is introduced by a dollar sign ($).

We can initialize a string array a notation that remembers the C one, but it uses round brackets to delimit the elements:
my @names = ("Tim", "Bill", "Jim");
Or we can use a more perl-ish one, that gets rid of quotations and commas:
my @names = qw(Tim Bill Jim);
The result is the same: we have defined an array containing three strings.

When we want to print an array, we usually print it using the interpolated notation:
print "@names\n";
Because the interpolation in case of arrays means inserting a blank between each element - making the result readable.

One may wonder what means assigning an array to a scalar variable:
my $len = @names;
As the chosen variable names should suggest, an array variable in a scalar context is actually evaluated to the length of the array itself. So this perl instruction:
print "$len: @names\n";
should result in an output like this:
3: Tim Bill Jim
We didn't actually need an explicit scalar variable to achieve that result, we could just tell to perl to use the array as a scalar, using the "scalar" operator:
print scalar @names, ": @names\n";

Chapter 3 of Beginning Perl by Simon Cozens is about arrays and associative arrays (hashes).

Go to the full post