【问题标题】:Can anyone explain why foreach worked but not map谁能解释为什么 foreach 有效但不能映射
【发布时间】:2012-07-13 03:09:39
【问题描述】:

如果键存在于 %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{$_});
}

【问题讨论】:

    标签: perl hash map exists


    【解决方案1】:

    您的问题是您的 map 表达式返回 undef @array 中的第一个元素的错误值。 当它被用作哈希键时,它被字符串化为一个空字符串。 (在 cmets Borodin 中指出这种解释是不正确的。事实上,空字符串来自错误值当键为“1”时从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'
            };
    

    【讨论】:

    • 非常快速且非常准确。
    • 您对undef 被字符串化为空字符串的解释是错误。您看到的空字符串是不存在的键的 exists $hash{$_} 值。 undef 不会在没有 Use of uninitialized value 警告噪音的情况下转换为空字符串。
    【解决方案2】:

    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 中的元素不作为 %hash 的键出现在列表中的单个空字符串 @ 987654334@返回。

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

      【讨论】:

        【解决方案4】:
        my %hash1 = map { ( $_ => $hash{$_} ) if exists($hash{$_}) } @array;
        

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

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

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

        或者您可以将过滤移出map

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

        【讨论】:

        • map 和 grep 的完美结合:D。谢谢
        猜你喜欢
        • 2010-09-19
        • 2013-04-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-14
        • 1970-01-01
        相关资源
        最近更新 更多