web developer & system programmer

coder . cl

ramblings and thoughts on programming...


function prototypes and references in perl

published: 20-08-2010 / updated: 20-08-2010
posted in: development, perl, programming, sysadmin, tips
by Daniel Molina Wegener

Some people is considering Perl as a non-friendly language, but in my opinion is a powerful language, not only for its capacity to do text-processing, it also has a lot of modules and extensions which makes it a language of choice for a wide variety of applications. I prefer to code Perl using strict and function prototypes, so it looks more clean and elegant. Also it supports lambdas for some tasks. Let’s take look on prototypes and references. You will find interesting topics reading perlsub(1) and perlref(1).

On the Perl language, prototypes usually are absent, since are not required. you just create a function — anonymous or not — by writing subs:

sub hello_world {
    return "hello world";
}

The principle of perl subs, is that you call them with an unique argument and that argument is a list, so that’s why calling functions in perl do not require the form function(arg1, arg2, arg3). For example you can call the hello_world function with four different forms:

print hello_world;        # the mos common form
print &hello_world;       # code reference style
print hello_world();      # the c derived form without arguments
print &hello_world();     # code reference style form without arguments

Like many languages, Perl provides for user-defined subroutines. These may be located anywhere in the main program, loaded in from other files via the do, require, or use keywords, or generated on the fly using eval or anonymous subroutines. You can even call a function indirectly using a variable containing its name or a CODE reference.

The last two ones are just indicating that the argument list is empty. Have you tried to pass an array or list as argument?

sub hello_who {
    my ($who) = @_;
    print "hello ${who}n";
}
my @nekos = ('neko', 'core', 'test');
hello_who 'neko';
hello_who @nekos;
hello_who (@nekos);

The code above will just print "hello neko" three times, since the argument handling is just requesting the first argument, and it’s equivalent to call my $who = shift;. So, to pass an array or list as argument, you need to pass its reference — which is handled internally as scalar type in perl:

sub hello_who_iterator($) {
    my ($who) = @_;
    foreach my $i (@{$who}) {
        print "hello ${i}n";
    }
}
my @nekos = ('neko', 'core', 'test');
hello_who_iterator @nekos;
hello_who_iterator @nekos, "hola";

The prototyped function above will allow you to handle one argument as reference and ignore the remaining argument, if any. The same happens to hashes, so you can pass three references without problems:

sub hello_world_three($$$) {
    my ($a, $h, $s) = @_;
    my @hello_l;
    foreach my $i (@{$a}) {
        if ($h->{$i}) {
            push @hello_l, "Hello ".$s.' '.$i.' '.$h->{$i};
        }
    }
    return join("n", @hello_l)."n";
}
my %saludos = ('Juan' => 'el "Gato"',
               'Pedro' => 'el "Epidemia"',
               'Diego' => 'el "Conejo"',);
my @saludar_a = ('Juan', 'Pedro');
print hello_world_three(@saludar_a, %saludos, 'Mr.');

But what happens if I pass directly those three arguments, without using their references? The example above will print the following text:

Hello Mr. Juan el "Gato"
Hello Mr. Pedro el "Epidemia"

If we replace the code to catch directly those arguments, and not their references, we will not get the same result and the function will not do anything…

sub hello_world_three {
    my (@a, %h, $s) = @_;
    my @hello_l;
    foreach my $i (@a) {
        if ($h{$i}) {
            push @hello_l, "Hello ".$s.' '.$i.' '.$h{$i};
        }
    }
    return join("n", @hello_l)."n";
}
my %saludos = ('Juan' => 'el "Gato"',
               'Pedro' => 'el "Epidemia"',
               'Diego' => 'el "Conejo"',);
my @saludar_a = ('Juan', 'Pedro');
print hello_world_three(@saludar_a, %saludos, 'Mr.');

The example above applies the shift function to the first argument, so the function just receives the @saludar_a argument, so %h and $s arguments will be empty. If you want to pass an array or a hash to a function, you must do it by passing their reference.

Any arguments passed in show up in the array @_. Therefore, if you called a function with two arguments, those would be stored in $_[0] and $_[1]. The array @_ is a local array, but its elements are aliases for the actual scalar parameters. In particular, if an element $_[0] is updated, the corresponding argument is updated (or an error occurs if it is not updatable). If an argument is an array or hash element which did not exist when the function was called, that element is created only when (and if) it is modified or a reference to it is taken. (Some earlier versions of Perl created the element whether or not the element was assigned to.) Assigning to the whole array @_ removes that aliasing, and does not update any arguments.


references

  • perlsub(1)
  • perlref(1)

No coments yet.

post a comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>