2.1. List Operators List操作符
There are several other list operators that you already know about from Learning Perl. The
sort operator puts its input list in order. In their theme song, the castaways don't come in
alphabetical order, but sort can fix that for us.
这里有一些其他的列表你已经在"Learning Perl"知道的操作符号,sort 操作符将他按照一定规则放到列表中,(后面不会翻译) 但是,sort 可以为我们完成此功能
my @castaways = sort qw(Gilligan Skipper Ginger Professor Mary-Ann);
The reverse operator returns a list in the opposite order.
reverse 操作符 返回的是与sort 相反的规则。
my @castaways = reverse qw(Gilligan Skipper Ginger Professor Mary-Ann);
2.1.1. List Filtering with grep 使用grep过滤的列表
The grep operator takes a list of values and a "testing expression." It takes one item after another in the list and places it into the $_ variable. It then evaluates the testing expression in a scalar context. If the expression evaluates to a true value, grep passes $_ on to the output list.
使用grep操作符 接受 一个值的列表和一个 “测试表达式” , grep 接受 在另一个个列表将其值存入到$_变量中的后的表达式,然后对上下文标量的“测试表达式”进行计算。如果,计算出的表达式是一个真值,那么grep 将$_ 写入到输出列表中。
my @lunch_choices = grep &is_edible($_), @gilligans_posessions.
# 此表达式的意思是: 当 @gilligans_posessions 中所有队列成员,依次赋予$_中,如果能够让 is_edible() 这个函数为真,那么,就将为真的$_ 赋给@lunch_choices .
In a list context, the grep operator returns a list of all such selected items. In a scalar context, grep returns the number of selected items.
在一个列表上下文中,grep 操作符所返回的全都是子项被筛选过的列表。在一标量上下文中,grep返回的是被筛选项的个数
my @results = grep EXPR, @input_list;
my $count = grep EXPR, @input_list;
Here,EXPR stands in for any scalar expression that should refer to $_ (explicitly or implicitly).For example, to find all the numbers greater than 10, in our grep expression we check if $_ is greater than 10.
这里,EXPR 代表应该在一个适用于 $_(明确的或不明确的) 的 任何一个标量表达式中。 例如,为了查询所有数字中大于10,在我们的表达式中,我们验证$_是否大于10
my @input_numbers = (1, 2, 4, 8, 16, 32, 64);
my @bigger_than_10 = grep $_ > 10, @input_numbers;
The result is just 16, 32, and 64. This uses an explicit reference to $_. Here's an example of an implicit reference to $_ from the pattern match operator
结果正是16,32和64,这就是使用明确的对$_的引用 。 这里有一个来自模式匹配操作符的非明确对$_引用的例子。
my @end_in_4 = grep /4$/, @input_numbers;
And now we get just 4 and 64.
现在我们得到的结果就是4和64;
While the grep is running, it shadows any existing value in $_, which is to say that grep borrows the use of this variable but puts the original value back when it's done. The variable $_ isn't a mere copy of the data item, though; it is an alias for the actual data element, similar to the control variable in a foreach loop.
If the testing expression is complex, we can hide it in a subroutine:
每当grep正在运行时,grep影射任何一个在$_中出现的值。这也就是说:grep只是借用这个变量 当所有他的要做的事完成后,但将初始值放回初始位置。
但是,变量$_不仅仅是从某一项中单纯的拷贝数据; 它是为了真实数据单元的一个别名,类似在foreach 循环中控制变量。
my @odd_digit_sum = grep digit_sum_is_odd($_), @input_numbers;
sub digit_sum_is_odd {
my $input = shift;
my @digits = split //, $input; # Assume no nondigit characters 假设没有非数字的字符 ,#如果$input 值是15,则数组中 @digits中就是1,5
my $sum;
$sum += $_ for @digits;
return $sum % 2;
}
#从上面的例子可以看出来如果return的返回值是0则 grep不返回后面列表中的单元,如果是非零则返回对应原值
# return 上面的例子函数过于复杂,我用简单例子表达一下,
my @input_numbers = (1, 2, 4, 8, 16, 32, 64);
#my @bigger_than_10 = grep $_ > 10, @input_numbers;
my @arr = grep odd($_),@input_numbers ;
print "a @arr \n";
sub odd
{
$t = shift ;
if ($t>4)
{
return $t ;
}
}
#最后结果是 8, 16, 32, 64 ,验证一下,就是grep只是做原来数据的过滤拷贝,但在拷贝过程中并不会原来数据进行任何的转换。
Now we get back the list of 1, 16, and 32. These numbers have a digit sum with a remainder of "1" in the last line of the subroutine, which counts as true.
The syntax comes in two forms, though: we just showed you the expression form, and now here's the block form. Rather than define an explicit subroutine that we'd use for only a single test, we can put the body of a subroutine directly in line in the grep operator, using the block forms
现在我们取回了1,16,32的列表,这些数据是函数正常运行且在子函数中最后一行数据汇总为1。
这个语法以两种形态出现,可是:我们仅仅向你展示了表达的形式,现在要介绍的是模块形式。 与其定义一个我们刚刚用来做单一测试的隐式的子程序,还不如我们可以子程序体直接将其放到grep操作符中,这也就是我们要介绍的模块形式。
In the block form of grep, there's no comma between the block and the input list. In the expression form of grep, there must be comma between the expression and the list.
在grep 的模块形式中, 在模块与输入列表之间没有逗号,在grep表达式中,表达式与列表之间必须要有逗号。
my @results = grep {
block;
of;
code;
} @input_list;
my $count = grep {
block;
of;
code;
} @input_list;
Just like the expression form, grep temporarily places each element of the input list into $_ . Next, it evaluates the entire block of code. The last evaluated expression in the block is the testing expression. (And like all testing expressions, it's evaluated in a scalar context.) Because it's a full block, we can introduce variables that are scoped to the block. Let's rewrite that last example to use the block form:
正如你所见到的表达式形式,grep 临时的位置所有输入的数组成员都进入到$_中, 接下来, grep 测试所有代码模块,最后,检测在在模块中的表达式。(正如其他表达是测试一样, 他是检测在上下文中的标量)因为,他全都是模块,所以我们可以在模块的范围中引入变量。为了使用模块形式,让我们从写刚才的例子。
my @odd_digit_sum = grep {
my $input = $_;
my @digits = split //, $input; # Assume no nondigit characters
my $sum;
$sum += $_ for @digits;
$sum % 2;
} @input_numbers;
Note the two changes: the input value comes in via $_ rather than an argument list, and we removed the keyword return. In fact, we would have been wrong to keep the return because we're no longer in a separate subroutine: just a block of code.[*] Of course, we can optimize a few things out of that routine since we don't need the intermediate variables:
注意这两个变化:输入值进入时经过了$_,而不是参数列表,并且我们去除了“return”关键字,事实上, 如果我们坚持写return那么结果会错的, 因为我们已经不再是在一个没关系的子程序中了:正如代码块! 当然,除了可以运行此程序,我们可以优化一些东西, 我们不再需要起中间媒介的变量。
my @odd_digit_sum = grep {
my $sum;
$sum += $_ for split //;
$sum % 2;
} @input_numbers; #擦,这程序写的也在简洁了!!!
Feel free to crank up the explicitness if it helps you and your coworkers understand and maintain the code. That's the main thing that matters.
你可以随便去提高程序的明确性,如果它有利于你和同事理解以及维护这段代码,这才是问题的关键。
2.1.2. Transforming Lists with map 带有map的转换数组。
The map operator has a very similar syntax to the grep operator and shares a lot of the same operational steps. For example, it temporarily places items from a list into $_ one at a time, and the syntax allows both the expression block forms.
map 操作符有与grep操作符十分相似的语法,并且共享了许多操作的步骤。例如: map中临时处的内容是来自一个数组,并同一时间加载进$_中,并且在语法上即允许表达式,同时允许模块形式。
However, the testing expression becomes a mapping expression. The map operator evaluates the expression in a list context (not a scalar context like grep). Each evaluation of the expression gives a portion of the many results. The overall result is the list concatenation of all individual results. In a scalar context, map returns the number of elements that are returned in a list context. But map should rarely, if ever, be used in anything but a list context.)
然而,测试表达式变成了一个map表达式,在map操作符检测一个数组内容表达式的时候(没有像grep以一样的标量内容)。每一个表达式的检测产生许多结果的部分。所有结果就每一个单独结果的串联。在一个标量上下文中,map返回元素的个数,还返回数组的内容。 map很少被使用,如果在一些地方使用过,那么返回的都是数组内容。
Let's start with a simple example:
然我们从一个练习开始吧。
my @input_numbers = (1, 2, 4, 8, 16, 32, 64);
my @result = map $_ + 100, @input_numbers;
For each of the seven items map places into $_, we get a single output result: the number that is 100 greater than the input number. So the value of @result is 101, 102, 104, 108, 116, 132, and 164.
But we're not limited to having only one output for each input. Let's see what happens when each input produces two output items:
7个单元内容都分别进入$_,我们得到一个单一的输出结果:比输入数据大100的数据,所以@result 的结果是101, 102, 104, 108, 116, 132, 和 164
我们没有被限制针对每一个输入就对应一个输出。接下来,让我们看看,当每一个输入对已产生两个输入内容时候会有什么样的情况发生。
my @result = map { $_, 3 * $_ } @input_numbers;
Now there are two items for each input item: 1, 3, 2, 6, 4, 12, 8, 24, 16, 48, 32, 96, 64, and 192. We can store those pairs in a hash, if we need a hash showing what number is three times a small power of two:
现在这有两个$_对应所有的输入内容:1, 3, 2, 6, 4, 12, 8, 24, 16, 48, 32, 96, 64, and 192. 我们可以将其存储到一个哈希中,如果我们需要一个哈希显示
两个数字中间,其中一个是另一个3倍的数据。
my %hash = @result;
Or, without using the intermediate array from the map: 或者,没有使用从map转换到数组的方式
my %hash = map { $_, 3 * $_ } @input_numbers;
You can see that map is pretty versatile; we can produce any number of output items for each input item. And we don't always need to produce the same number of output items. Let's see what happens when we break apart the digits:
你可以看出来map是非常实用的;我们为每一个输入内容生成任何一个输入内容的数据,并且,我们不需要总是生成相同的输入内容。让我们看看当我们那数字会有什么样的事情发生。
my @result = map { split //, $_ } @input_numbers;
The inline block of code splits each number into its individual digits. For 1, 2, 4, and 8, we get a single result. For 16, 32, and 64, we get two results per number. When map concatenates the results lists, we end up with 1, 2, 4, 8, 1, 6, 3, 2, 6, and 4. If a particular invocation results in an empty list, map concatenates that empty result into the larger list, contributing nothing to the list. We can use this feature to select and reject items.For example, suppose we want only the split digits of numbers ending in 4:
在内部代码模块中分离每一个数字到它自己独立的数字中。就1,2,4,8 ,我们的到的都是单一的结果,就16,32,64,每一个数字我们得到两个结果。当map连接结果列表的时候,我们最终得到1, 2, 4, 8, 1, 6, 3, 2, 6, 和 4 ,如果一个特殊调用的结果中为空列表,map连接空的结果到更大的列表中,对于这个列表没有起到任何作用。我们可以使用这个特性去选择和排除列表中的内容。例如,假设,我们仅仅使数字数字的数目截至到4.
my @result = map {
my @digits = split //, $_;
if ($digits[-1] = = 4) {
@digits;
} else {
( );
}
} @input_numbers;
If the last digit is 4, we return the digits themselves by evaluating @digits (which is in list context). If the last digit is not 4, we return an empty list, effectively removing results for that particular item. Thus, we can always use a map in place of a grep, but not vice versa. Of course, everything we can do with map and grep, we can also do with explicit foreach loops. But then again, we can also code in assembler or by toggling bits into a front panel.[*] The point is that proper application of grep and map can help reduce the complexity of the program, allowing us to concentrate on high-level issues rather than details.