任何人都可以解释为什么foreach工作但不映射

时间:2012-07-13 09:18:12

标签: perl hash map exists

如果密钥存在于%hash中,我试图将键值对放在%hash1上 数组中有一个元素,%hash没有条目 例如:@array =(1,2,3,4,5); #there在%hash

处没有密钥1的哈希条目

所以我认为map会完成这项工作,我会在我的新哈希中获得4个键,即%hash1,但它会提供5个键。与此同时,我尝试了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{$_});
}

4 个答案:

答案 0 :(得分:9)

您的问题是map表达式为undef中的第一个元素返回 @array 错误值。 并将其字符串化为空字符串,因为它被用作散列键。 (在评论中,Borodin指出这种解释是不正确的。实际上空字符串来了从密钥为&#34; 1&#34;)的exists返回的假值<)>

如果您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 :(得分:4)

map将始终返回您在其代码块中放置的内容。所以

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

$_=>$hash{$_}存在时为$hash{$_},如果不存在,则为""

您可能想写的内容:

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

答案 2 :(得分: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""的true或false,因此@array中的元素不会显示为%hash结果的键在map返回的列表中的单个空字符串中。

如果将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

使用my %hash1 = map { exists $hash{$_} ? ( $_ => $hash{$_} ) : () } @array 编写它的方法是使用条件运算符,以便在条件失败时返回空列表

foreach

我相信您不需要解释为什么return循环有效?

我相信wantarray在所有块中都有效,就像子程序中允许的那样。 {{1}}在这里已经有效,并且这是一个特定的限制,它禁止块一般退出并返回一个显式值。

答案 3 :(得分: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;