天天看點

Intermediate Perl 1 Chapter 2. Intermediate Foundations 2.1. List Operators List

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.

繼續閱讀