Perl 有人能解释为什么foreach工作而不是map吗

Perl 有人能解释为什么foreach工作而不是map吗,perl,hash,map,exists,Perl,Hash,Map,Exists,如果%hash中存在键,我尝试将键值对放在%hash1 数组中有一个元素在%hash处没有条目 例如:@array=(1,2,3,4,5)#在%hash处没有键1的哈希项 所以我认为map可以完成这项工作,我将在我的新哈希中得到4个键,即%hash1,但它给出了5个键。同时,我尝试了foreach,它成功了。我误以为我们可以用map替换foreach,但这个案例让我思考。 谁能解释一下,我的逻辑哪里出错了 #Method 1. Comment it while using Method 2 %h

如果%hash中存在键,我尝试将键值对放在%hash1 数组中有一个元素在%hash处没有条目 例如:@array=(1,2,3,4,5)#在%hash处没有键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{$_});
}

您的问题是
map
表达式为
@array
中的第一个元素返回了一个假值。当它被用作散列键时,它被字符串化为一个空字符串。(在注释中,Borodin指出这种解释是不正确的。事实上,空字符串来自当键为“1”时从
exists
返回的假值)

如果a)打开
strict
warnings
和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
        };
#!/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;
您正在生成一个包含奇数个元素的列表。这并不是一个快乐的杂烩

在构建散列时,需要确保元素数为偶数。因此,当您使用
map
时,您需要为每个迭代返回零个或两个元素。所以你需要这样的东西:

$ ./hash 

Odd number of elements in hash assignment at ./hash line 12.
$VAR1 = {
          '' => 2,
          'three' => 4,
          'five' => undef,
          'two' => 3,
          'four' => 5
        };
#!/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'
        };
将始终返回您在其代码块中输入的内容。那么

%hash1 = map { $_=>$hash{$_}  if(exists $hash{$_}) } @array;
$hash{$}
存在时,将是
$\u=>$hash{$\u}
,如果不存在,将是

你可能想写的是:

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

map
调用的块针对提供的列表中的每个值进行计算,该块返回的值是最后计算的表达式的值

您的
map
语句

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

my %hash1 = map {
  if (exists $hash{$_}) {
    $_ => $hash{$_}
  }
} @array
因此首先计算表达式
exists$hash{$}
。然后,如果为true,则计算
$\=>$hash{$\}

因此,如果测试成功,则最后计算的表达式是
$\u=>$hash{$\u}
,这是您想要的,但是如果测试失败,则块返回
exists$hash{$\u}
的值

exists
返回
1
表示true或false,因此
@array
中未显示为
%hash
键的元素会在
map
返回的列表中产生一个空字符串

如果将
映射指定给数组,则更容易查看该映射的结果。这样可以避免散列赋值中出现奇数个
元素的警告和自动赋值
unde
散列值

如果你改写

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
在这里已经有效,这是一个特定的限制,通常禁止块退出并返回显式值

my %hash1 = map { ( $_ => $hash{$_} ) if exists($hash{$_}) } @array;
是同一件事吗

my %hash1 = map { exists($hash{$_}) and ( $_ => $hash{$_} ) } @array;
考虑当
存在($hash{$\u})
为false时会发生什么。当不应返回任何值时,将返回单个值(
dualvar(0,”)
aka“false值”)。如果
存在
为false,则可以更改表达式以返回空列表

my %hash1 = map { exists($hash{$_}) ? ( $_ => $hash{$_} ) : () } @array;
或者,您可以将筛选从
映射中移出

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

非常快速且非常准确。您关于将
unde
字符串化为空字符串的解释是错误的。您看到的空字符串是不存在键的
exists$hash{$\u}
的值<代码>未定义
如果不使用未初始化值
警告噪声,将不会转换为空字符串。map和grep:D的良好组合。谢谢