Learning Perl Objects, References amp;amp; Modules [Electronic resources] نسخه متنی

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

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

Learning Perl Objects, References amp;amp; Modules [Electronic resources] - نسخه متنی

Randal L. Schwartz

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

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

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














5.6 Applying a Bit of Indirection


Some
problems that may appear very complex are actually simple once
you've seen a solution or two. For example, suppose
you want to find the items in a list that have odd digit sums but
don't want the items themselves. What you want to
know is where they occurred in the original list.

All that's required is a bit of
indirection.[6]

[6] A famous computing maxim states that
"there is no problem so complex that it cannot be
solved with appropriate additional layers of
indirection." Of course, with indirection comes
obfuscation, so there's got to be a magic middle
ground somewhere.


First, you have a selection
problem, so you use a grep. Let's
not grep the values themselves but the index for
each item:

my @input_numbers = (1, 2, 4, 8, 16, 32, 64);
my @indices_of_odd_digit_sums = grep {
...
} 0..$#input_numbers;

Here, the expression 0..$#input_numbers will be a
list of indices for the array. Inside the block,
$_ is a small integer, from 0 to 6 (seven items
total). Now, you don't want to decide whether
$_ has an odd digit sum. You want to know whether
the array element at that index has an odd digit sum. Instead of
using $_ to get the number of interest, use
$input_numbers[$_]:

my @indices_of_odd_digit_sums = grep {
my $number = $input_numbers[$_];
my $sum;
$sum += $_ for split //, $number;
$sum % 2;
} 0..$#input_numbers;

The result will be the indices at which 1, 16, and 32 appear in the
list: 0, 4, and 5. You could use these indices in an array slice to
get the original values again:

my @odd_digit_sums = @input_numbers[ @indices_of_odd_digit_sums ];

The strategy here for an indirect grep or
map is to think of the $_
values as identifying a particular item of interest, such as the key
in a hash or the index of an array, and then use that identification
within the block or expression to access the actual values.

Here's another example: select the elements of
@x that are larger than the corresponding value in
@y. Again, you'll use the indices
of @x as your $_ items:

my @bigger_indices = grep {
if ($_ > $#y or $x[$_] > $y[$_]) {
1; # yes, select it
} else {
0; # no, don't select it
}
} 0..$#x;
my @bigger = @x[@bigger_indices];

In the grep,
$_ varies from 0 to the highest index of
@x. If that element is beyond the end of
@y, you automatically select it. Otherwise, you
look at the individual corresponding values of the two arrays,
selecting only the ones that meet your match.

However, this is a bit more verbose than it needs to be. You could
simply return the boolean expression rather than a separate 1 or 0:

my @bigger_indices = grep {
$_ > $#y or $x[$_] > $y[$_];
} 0..$#x;
my @bigger = @x[@bigger_indices];

More easily, you can skip the step of
building the intermediate array by simply returning the items of
interest with a map:

my @bigger = map {
if ($_ > $#y or $x[$_] > $y[$_]) {
$x[$_];
} else {
( );
}
} 0..$#x;

If the index is good, return the resulting array value. If the index
is bad, return an empty list, making that item disappear.



/ 199