הלינקייה: מגזין חודשי למפתחים

רוצה לשמוע על כל האירועים, המדריכים, הקורסים והמאמרים שנכתבו החודש ?
הלינקייה הינו מגזין חופשי בעברית שמשאיר אותך בעניינים.
בלי ספאם. בלי שטויות. פעם בחודש אצלך בתיבה.

Subroutines

In any language, subroutines allow code reuse by taking a block of code and "naming" it. that code block is called a subroutine. Perl subroutines are special when compared to other languages, in that they don't declare expected arguments - The caller can pass in anything they like. This has its advantages and disadvantages, as we should see in the lesson.

Subroutines are defined with the sub keyword followed by a block of code. Inside that block, a special list called @_ holds all the arguments passed to the subroutine. We usually extract arguments from it by copying it into another list or using shift.
By convention, subroutine names in perl are all lower cased, and the _ separates words if more than one is required.

use strict;
use warnings;
use Carp;
 
# This function takes one scalar argument and 
# returns the sum of its digits as a scalar
sub sum_of_digits {
    use integer;
    # note the () around $number. Can you guess what would happen if we forgot them ?
    my ($number) = @_;
 
    my $sum = 0;
    while ($number) {
        my $digit = $number % 10;
        $sum      += $digit;
        $number /= 10;
    }
 
    return $sum;
}
 
# The say function prints everything it got
# and adds a newline character at the end
sub say {
    print @_, "\n";
}
 
sub pow {
    my ($base, $exp) = @_;
    croak 'Missing argument: base' if ! defined $base;
    croak 'Missing argument: $exp' if ! defined $exp; 
 
    return $base ** $exp;
}
 
# Subroutines are also used to define constants. 
# Since the return value of a subroutine is the result of the last line, this shorthand works:

sub PI { 3.14 }
sub RC_OK { 1 }

Perl has three scoping keywords, each with its own meaning. Examine the following:

use Modern::Perl;
 
sub surprise1 {
        our $n = shift;
        return surprise1($n-1) * $n if $n > 1;
        return 1;
}
 
sub surprise2 {
        my $n = shift;
        return surprise2($n-1) * $n if $n > 1;
        return 1;
}
 
sub surprise3 {
        our $n;
        local $n = shift;
        return surprise3($n-1) * $n if $n > 1;
        return 1;
}
 
say "surprise1(4) = ", surprise1(4);
say "surprise2(4) = ", surprise2(4);
say "surprise3(4) = ", surprise3(4);

Validations

Perl is a dynamically typed language, and so there is no argument type validation during compile time. The bright side of this, is we can write amazingly flexible subroutines (like we saw in the examples above) easily.
Of course, it's not always possible to eat everything, and so a common perl practice is to check the input arguments at the beginning of a subroutine to make sure we're ok with them.
The use of Carp here is optional but helpful. Carp provides us with nicer error messages in case something goes wrong, including the full caller's line that had passed the wrong arguments. Since Carp is a core module, there's no reason not to use it.
Here's how it's done.

use strict;
use warnings;
use Carp;


sub take_positive_number {
  my $num = shift;

  croak "Invalid argument: $num" unless $num > 0;  
}

sub take_four_arguments_and_one_optional {
  my $first       = shift or croak "Missing first";
  my $second = shift or croak "Missing second";
  my $third      = shift or croak "Missing third";
  my $fourth    = shift or croak "Missing fourth";
  my $fifth       = shift;
}

course: