2

如果键存在于 %hash 中,我尝试将键值对放在 %hash1 数组中有一个元素在 %hash 处没有条目 例如:@array = (1,2,3,4,5); #在 %hash 处没有 key 1 的哈希条目

所以我认为 map 可以完成这项工作,我将在我的新哈希中获得 4 个键,即 %hash1 但它提供了 5 个键。同时我尝试了 foreach 并且它起作用了。我曾幻想我们可以使用 map 替换 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

4 回答 4

9

您的问题是您的map表达式undef@array. 当它被用作哈希键时,它被字符串化为一个空字符串。 (Borodin 在评论中指出这种解释是不正确的。实际上空字符串来自exists键为“1”时返回的假值)

如果您 a) 打开并且 b) 在创建哈希后使用它来显示哈希,您可能会更好地了解正在strictwarnings什么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'
        };
于 2012-07-13T09:44:44.180 回答
4

map将始终返回您放入其代码块中的内容。所以返回值为

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

将是$_=>$hash{$_}何时$hash{$_}存在,""如果它不存在。

你可能想写的:

my %hash1 = map { exists($hash{$_}) ? ($_ => $hash{$_}) : () }
于 2012-07-13T09:45:02.173 回答
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返回true1""false,因此@array不作为键出现的元素会在返回%hash的列表中产生单个空字符串map

map如果将 a 分配给数组,则更容易看到 a 的结果。这样您就可以避免警告和哈希值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在这里已经有效,并且它是一个特定的限制,通常禁止块退出并返回显式值。

于 2012-07-13T10:19:34.887 回答
2
my %hash1 = map { ( $_ => $hash{$_} ) if exists($hash{$_}) } @array;

是一样的

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

考虑当exists($hash{$_})为假时会发生什么。dualvar(0,"")当不应该返回单个值(又名“假值”)。您可以更改表达式以在exists为 false时返回空列表

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

或者您可以将过滤移出map

my %hash1 = map { $_ => $hash{$_} } grep { exists($hash{$_}) } @array;
于 2012-07-13T18:07:43.127 回答