2012-07-13 76 views
2

我試圖把鍵值對%hash1如果密鑰存在%哈希 有一個元素在數組中沒有條目在%hash 例如:@array =(1,2,3,4,5);所以我認爲map會做這項工作,我會在我的新哈希中得到4個鍵,即%hash1,但它提供了5個鍵。同時我嘗試了foreach並且它工作。我錯覺我們可以用map來替換foreach,但這個案例讓我思考。 任何人都可以解釋,我的邏輯出錯了嗎?任何人都可以解釋爲什麼foreach工作,但不是地圖

#Method 1. Comment it while using Method 2 
%hash1 = map { $_=>$hash{$_} if(exists $hash{$_}) } @array; 

# Method 2. Comment whole loop while using method 1 
foreach (@array){ 
    $hash1{$_} = $hash{$_} if(exists $hash{$_}); 
} 

回答

9

你的問題是,你的map表達式返回 undef @array第一元素的假值。 然後將它用作散列鍵,將其字符串化爲空字符串。 (在評論鮑羅廷指出,這種解釋是不正確。事實上空字符串來自於從exists返回時,關鍵是「1」的假值)

你可能會得到更好的主意如果你a)開啓了strictwarnings和b)使用Data::Dumper在你創建它之後顯示哈希值。

#!/usr/bin/perl 

use strict; 
use warnings; 
use 5.010; 

use Data::Dumper; 

my @array = (1 .. 5); 
my %hash = (2 => 'two', 3 => 'three', 4 => 'four', 5 => 'five'); 

my %hash1 = map { $_=>$hash{$_} if(exists $hash{$_}) } @array; 

say Dumper \%hash1; 

這表明你結束了這樣的哈希:

$ ./hash 

Odd number of elements in hash assignment at ./hash line 12. 
$VAR1 = { 
      '' => 2, 
      'three' => 4, 
      'five' => undef, 
      'two' => 3, 
      'four' => 5 
     }; 

您正在生成具有奇數個元素的列表。這並不能令人滿意。

當你構建一個散列時,你需要確保你有一個偶數個元素。因此,當您使用map時,您需要爲每次迭代返回零個或兩個元素。所以你需要這樣的東西:

#!/usr/bin/perl 

use strict; 
use warnings; 
use 5.010; 

use Data::Dumper; 

my @array = (1 .. 5); 
my %hash = (2 => 'two', 3 => 'three', 4 => 'four', 5 => 'five'); 

my %hash1 = map { exists $hash{$_} ? ($_ => $hash{$_}) :() } @array; 

say Dumper \%hash1; 

請注意,我們顯式返回一個空列表,當在第一個哈希中找不到鍵。

$ ./hash2 
$VAR1 = { 
      '4' => 'four', 
      '3' => 'three', 
      '2' => 'two', 
      '5' => 'five' 
     }; 
+1

非常快,非常準確的。 – Jassi 2012-07-13 10:02:48

+7

你對'undef'被串接爲空字符串的解釋是錯誤的*。您看到的空字符串是不存在的鍵的存在$ hash {$ _}的值。如果沒有使用未初始化值的警告噪聲,undef不會被轉換爲空字符串。 – Borodin 2012-07-13 10:26:43

+0

謝謝。糾正。 – 2012-07-13 14:00:40

4

map將始終返回您放入其代碼塊的內容。因此,對於

%hash1 = map { $_=>$hash{$_} if(exists $hash{$_}) } @array; 

的返回值將是$_=>$hash{$_}$hash{$_}存在,""如果它不存在。

你可能想寫什麼:

my %hash1 = map { exists($hash{$_}) ? ($_ => $hash{$_}) :() } 
3

一個map調用的塊中所提供的列表中的每個值進行評估,如塊返回的值是計算的最後一個表達式的值。

map聲明

my %hash1 = map { $_ => $hash{$_} if (exists $hash{$_}) } @array 

相當於

my %hash1 = map { 
    if (exists $hash{$_}) { 
    $_ => $hash{$_} 
    } 
} @array 

所以首先表達exists $hash{$_}評估的。那麼,如果這是真的,則評估$_ => $hash{$_}

如果測試成功,則最後評估的表達式因此爲$_ => $hash{$_},這正是您想要的,但如果測試失敗,塊返回值爲exists $hash{$_}

exists返回或者1""爲真或假,所以在@array元素不出現在列表中map回報%hash結果在一個空字符串的關鍵。

如果將其分配給數組,則更容易看到map的結果。這樣,您可以避免Odd number of elements in hash assignment警告以及undef散列值的自動分配。

如果你寫的,而不是

my @arr = map { $_ => $hash{$_} if (exists $hash{X}) } @array; 

(即測試總是失敗)的結果是一樣的,即

my @arr = map { exists $hash{X} } @array; 

或只是

("", "", "", "") 

方式用map來寫這個就是用condit有理操作,從而如果條件不滿足

my %hash1 = map { exists $hash{$_} ? ($_ => $hash{$_}) :() } @array 

我相信你不需要爲什麼你foreach循環作品的解釋返回一個空列表?

我相信在子程序中允許return在所有塊內都是有效的。 wantarray在這裏已經有效,它是一個特定的限制,一般禁止塊退出並返回一個顯式值。

2
my %hash1 = map { ($_ => $hash{$_}) if exists($hash{$_}) } @array; 

my %hash1 = map { exists($hash{$_}) and ($_ => $hash{$_}) } @array; 

考慮時會發生什麼exists($hash{$_})是假的一樣的東西。單個值(dualvar(0,"")又名「假值」)在沒有任何值時返回。你既可以改變表達式返回一個空列表時exists是假

my %hash1 = map { exists($hash{$_}) ? ($_ => $hash{$_}) :() } @array; 

,或者你可以移動過濾出map

my %hash1 = map { $_ => $hash{$_} } grep { exists($hash{$_}) } @array; 
+0

更簡單的解釋。 – ikegami 2012-07-13 18:09:56

+0

地圖和grep的好組合:D。謝謝 – Jassi 2012-07-20 08:25:22

相關問題