Workshop

The workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to give you experience in using what you've learned. Try and understand the quiz and exercise answers before you go on to tomorrow's lesson.

Quiz

1:Name two reasons why subroutines are useful.
A1: Subroutines are useful for a number of reasons:
  • They help break up a large script into smaller bits to help manage complexity

  • Procedures in subroutines can be referred to by name to make scripts more readable

  • Subroutines can define local variables that are more efficient, easier to manage and control than globals

  • If you use the same code repeatedly throughout your programs, you can put that code into a subroutine, and then just reuse it

  • Smaller chunks of scripts can be more easily developed and debugged

2:Show how to call a subroutine.
A2: Call a subroutine using one of these forms:
&thisubroutine();    # both % and parens
&thissubroutine(1,2); # with arguments
thissubroutine(1,2);  # & is optional

3:Show how to define a subroutine.
A3: Define a subroutine like this:
sub name {
   # body of subroutine
}

4:Does it matter whether or not a subroutine is defined before it's called? Where in your Perl script should subroutine definitions appear?
A4: For most instances, you can call a subroutine before defining it, and vice versa. Perl is not particular about predefining subroutines.

You can define a subroutine anywhere a regular Perl statement can go, although, typically, subroutines are defined in a group at the beginning or end of a Perl script.

5:If you don't include an explicit call to return in your script, what value does a subroutine return?
A5: Without an explicit return, subroutines return the last value that was evaluated in the block.
6:Is the argument that gets returned from the subroutine with return a scalar or a list value?
A6: Trick question! Return can be used to return either a scalar or a list, depending on what you want to return.
7:In which parts of a Perl script are variables declared using my available?
A7: My variables are only available to code inside the nearest enclosing block and not to any nested subroutines.
8:What happens when you declare a my local variable with the same name as a global variable?
A8: Variables declared with my that have the same names as global variables will hide the value of the global. Once the subroutine or other block is finished executing, the original value of the global will be restored.
9:What happens to a hash you pass into a subroutine?
A9: When you pass any list into a subroutine, it is flattened into a single list of scalar values. In the case of hashes, this means unwinding the hash into its component keys and values (the same way a hash is handled in any general list context).
10:What is @_ used for? Where is it available?
A10: @_ refers to the argument list of the current subroutine. It's most commonly used inside a subroutine definition, although you can also define is as a global variable (see “Going Deeper”).
11:How do you name parameters in a Perl subroutine?
A11: Perl does not have formal named parameters. You can extract elements from the argument list @_ and assign them to local variables in the body of your subroutine (although once assigned to local values, they cease to be references to the same values outside that subroutine).
12:What does wantarray do? Why would you want to use it?
A12: The wantarray function allows you to find out the context in which your subroutine was called (scalar or list) to provide different behavior or return a sensible result.

Exercises

1:Write a subroutine that does nothing but print its arguments, one argument per line, with each line numbered.
A1: Here's one answer:
sub printargs {
    my $line = 1;
    foreach my $arg (@_) {
        print "$line. $arg
";
        $line++;
    }
}

2:Write a subroutine that takes a string as an argument and returns the string in reverse order by words.
A2: Here's one answer:
sub reverstring {
    my @str = split(/s+/, $_[0]);
    return join(" ", reverse @str);
}

3:Write a subroutine that takes a list of numbers and squares them all, returning the list of squares. If there are elements in the string that are not numbers, delete them from the final list.
A3: Here's one answer:
sub squares {
    my @final = ();
    foreach my $el (@_) {
        if ($el !~ /D+/) {
            @final = (@final, $el**2);
        }
    }
    return @final;
}

4:BUG BUSTER: What's wrong with this script?
@int = &intersection(@list1, @list2);

sub intersection {
   my (@1, @2) = @_;
   my @final = ();
   OUTER: foreach my $el1 (@one) {
      foreach my $el2 (@two) {
         if ($el1 eq $el2) {
             @final = (@final, $el1);
             next OUTER;
         }
      }
   }
   return @final;
}

A4: The fallacy in this script is assuming that the two lists, @list1 and @list2, will remain as two lists inside the body of the subroutine. Because Perl combines all list arguments into a single list, you cannot retain individual lists inside the body of the subroutine. Consider using globals instead.
5:BUG BUSTER: How about this one?
$numtimes = &search(@input, $key);

sub search {
   my (@in, $key) = @_;
   my $count = 0;
   foreach my $str (@in) {
       while ($str =~ /$key/og) {
           $count++;
       }
   }
   return $count;
}

A5: The problem with this script results because of the order of the arguments. The list and scalar arguments are flattened into a single list on their way into the subroutine. The failure results with the line that assigns the local variables @in and $key to the argument list in @_; because @in is a list it will copy all the elements in @_, and leave none left for $key. By simply reversing the order of the arguments (put the key first, then the list), you'll get around this problem.
6:Write a subroutine that, when used in a scalar context, reads in a single line of input and reverses it (character by character this time). When used in a list context, reads in multiple lines of input into a list and reverses all the lines (last line first, and so on—the individual strings don't have to be reversed).
A6: Here's one answer:
sub rev {
    my $in = "";
    if (wantarray()) {  # list context
        my @inlist = ();
        while () {
            print 'Enter input: ';
            $in = <STDIN>;
            if ($in ne "
") {
                @inlist = ($in, @inlist); # reverse order
            }
            else { last; }
        }
        return @inlist;
    }
    else {  # scalar context
        print 'Enter input: ';
        chomp($in = <STDIN>);
        return reverse $in;
    }
}

7:Take the image extractor script we wrote yesterday and turn it into a script that uses subroutines.
A7: Here's one approach:
#!/usr/local/bin/perl -w

$/ = "";     # paragraph input mode

while (<>) {
    while (/<IMGs([^>]+)>/ig) {
        &processimg($1);
    }
}

sub processimg {
    my $raw = $_[0];
    my %atts = ();

    while ($raw =~ /([^s=]+)s*=s*("([^"]+)"|[^s]+s*)/ig) {
        if (defined $3) {
            $atts{ uc($1) } = $3;
        } else { $atts{ uc($1)} = $2; }
    }
    if ($raw =~ /ISMAP/i) {
        $atts{'ISMAP'} = "Yes";
    }
    &printatts(%atts);
}

sub printatts {
    my %atts = @_;

    print '-' x 15;
    print "
Image:  $atts{'SRC'} 
";
    foreach my $key ("WIDTH", "HEIGHT",
                      "BORDER", "VSPACE", "HSPACE",
                      "ALIGN", "ALT", "LOWSRC", "ISMAP") {
        if (exists($atts{$key} )) {
            $atts{$key} =~ s/[s]*
/ /g;
            print "   $key: $atts{$key} 
";
        }
    }
}

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset