Perl Cd Bookshelf [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Perl Cd Bookshelf [Electronic resources] - نسخه متنی

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید

A.14. Answers to Chapter 15 Exercises



  1. Here's one way to do it:

    my @numbers;
    push @numbers, split while <>;
    foreach (sort { $a <=> $b } @numbers) {
    printf "%20g\n", $_;
    }

    That second line of code is too confusing, isn't it? Well, we
    did that on purpose. Although we recommend that you write clear code,
    some people like writing code that's as hard to understand as
    possible,[398] so we want you to be prepared for the worst. Someday,
    you'll need to maintain confusing code like this.

    [398]Well, we don't recommend it for
    normal coding purposes, but it can be a fun game
    to write confusing code, and it can be educational to take someone
    else's obfuscated code examples and spend a weekend or two
    figuring out just what they do. If you want to see some fun snippets
    of such code and maybe get a little help with decoding them, ask
    around at the next Perl Mongers' meeting. Or search for JAPHs
    on the Web, or see how well you can decipher the example obfuscated
    code block near the end of this chapter's answers.


    Since that line uses the while modifier,
    it's the same as if it were written in a loop like this:

    while (<>) {
    push @numbers, split;
    }

    That's better, but maybe it's still a little unclear.
    (Nevertheless, we don't have a quibble about writing it this
    way. This one is on the correct side of the "too hard to
    understand at a glance" line.) The while
    loop is reading the input a line at a time (from the user's
    choice of input sources, as shown by the diamond operator), and
    split is, by default, splitting that on whitespace
    to make a list of words -- or, in this case, a list of numbers.
    The input is just a stream of numbers separated by whitespace, after
    all. Either way that you write it, then, that
    while loop will put all of the numbers from the
    input into @numbers.

    The foreach loop takes the sorted list and prints
    each one on its own line, using the %20g numeric
    format to put them in a right-justified column. You could have used
    %20s instead. What difference would that make?
    Well, that's a string format, so it would have left the strings
    untouched in the output. Did you notice that our sample data included
    both 1.50 and 1.5, and both
    04 and 4? If you printed those
    as strings, the extra zero characters will still be in the output;
    but %20g is a numeric format, so equal numbers
    will appear identically in the output. Either format could
    potentially be correct, depending upon what you're trying to
    do.



  2. Here's one way to do it:

    # don't forget to incorporate the hash %last_name,
    # either from the exercise text or the downloaded file
    my @keys = sort {
    "\L$last_name{$a}" cmp "\L$last_name{$b}" # by last name
    or
    "\L$a" cmp "\L$b" # by first name
    } keys %last_name;
    foreach (@keys) {
    print "$last_name{$_}, $_\n"; # Rubble,Bamm-Bamm
    }

    There's not much to say about this one; we put the keys in
    order as needed, then print them out. We chose to print them in
    last-name-comma-first-name order just for fun; the exercise
    description left that up to you.



  3. Here's one way to do it:

    print "Please enter a string: ";
    chomp(my $string = <STDIN>);
    print "Please enter a substring: ";
    chomp(my $sub = <STDIN>);
    my @places;
    for (my $pos = -1; ; ) { # tricky use of three-part for loop
    $pos = index($string, $sub, $pos + 1); # find next position
    last if $pos == -1;
    push @places, $pos;
    }
    print "Locations of '$sub' in '$string' were: @places\n";

    This one starts out simply enough, asking the user for the strings
    and declaring an array to hold the list of substring positions. But
    once again, as we see in the for loop, the code
    seems to have been "optimized for cleverness", which
    should be done only for fun, never in production code. But this
    actually shows a valid technique which could be useful in some cases,
    so let's see how it works.

    The my variable $pos is
    declared private to the scope of the for loop, and
    it starts with a value of -1. So as not to keep
    you in suspense about this variable, we'll tell you right now
    that it's going to hold a position of the substring in the
    larger string. The test and increment sections of the
    for loop are empty, so this is an infinite loop.
    (Of course, we'll eventually break out of it, in this case with
    last).

    The first statement of the loop body looks for the first occurrence
    of the substring at or after position $pos + 1.
    That means that on the first iteration, when $pos
    is still -1, the search will start at position
    0, the start of the string. The location of the
    substring is stored back into $pos. Now, if that
    was -1, we're done with the
    for loop, so last breaks out of
    the loop in that case. If it wasn't -1, then
    we save the position into @places and go around
    the loop again. This time, $pos + 1 means that
    we'll start looking for the substring just after the previous
    place where we found it. And so we get the answers we wanted and the
    world is once again a happy place.

    If you didn't want that tricky use of the
    for loop, you could accomplish the same result as
    shown here:

    {
    my $pos = -1;
    while (1) {
    ... # Same loop body as the for loop used above
    }
    }

    The naked block on the outside restricts the scope of
    $pos. You don't have to do that, but
    it's often a good idea to declare each variable in the smallest
    possible scope. This means we have fewer variables
    "alive" at any given point in the program, making it less
    likely that we'll accidentally reuse the name
    $pos for some new purpose. For the same reason, if
    you don't declare a variable in a small scope, you should
    generally give it a longer name that's thereby less likely to
    be reused by accident. Maybe something like
    $substring_position would be appropriate in this
    case.

    On the other hand, if you were trying to
    obfuscate your code (shame on you!), you could create a monster like
    this (shame on us!):

    for (my $pos = -1; -1 != 
    ($pos = index
    +$string,
    +$sub,
    +$pos
    +1
    );
    push @places, ((((+$pos))))) {
    'for ($pos != 1; # ;$pos++) {
    print "position $pos\n";#;';#' } pop @places;
    }

    That even trickier code works in place of the original tricky
    for loop. By now, you should know enough to be
    able to decipher that one on your own, or to obfuscate code in order
    to amaze your friends and confound your enemies. Be sure to use these
    powers only for good, never for evil.

    Oh, and what did you get when you searched for t
    in This is a test.? It's at positions
    10 and 13. It's not at
    position 0; since the capitalization doesn't
    match, the substring doesn't match.



/ 875