2012-07-13 72 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

相关问题