【问题标题】:Define Perl while() = each {} loop with inline hash使用内联哈希定义 Perl while() = 每个 {} 循环
【发布时间】:2018-06-17 15:56:18
【问题描述】:

我试图在每个循环的一段时间内定义一个内联哈希,我的程序没有抛出任何错误,但也没有执行打印语句。是否可以定义如下的内联哈希:

while (my (key, value) = each %{ (apple  => "red", orange => "orange", grape  => "purple")}) { 
    print "something";
}

或者,如果我直接在每个语句中调用一个返回哈希值的 sub,如下所示:

sub returnsHash {
    my %fruits = (
        apple  => "red",
        orange => "orange",
        grape  => "purple",
    );
    return %fruits;
}

while (my (key, value) = each %{ returnsHash() }) { 
    print "something";
}

【问题讨论】:

  • returnsHash 的输出是什么?
  • 它可以是任何返回哈希的东西,编辑了我的问题
  • 始终使用use strict; use warnings qw( all );!!!它会识别出您所询问的问题。
  • Re "它可以是任何返回散列的东西,编辑了我的问题",你问题中的returnHash 不返回散列;它返回 6 个字符串。事实上,不可能返回一个哈希值。 sub 只能返回标量。 sub 可以返回对哈希的引用,但这不是您所说或所说的。

标签: perl


【解决方案1】:

标量上下文中的列表/逗号运算符计算为在标量上下文中计算的最后一项的结果。这意味着

each %{ apple => "red", orange => "orange", grape  => "purple" }

等价于

each %{ "purple" }

这是你的两个 sn-ps 正在做的事情,但这是不受欢迎的,而且是严格的违规行为。 (总是使用use strict; use warnings qw( all );!!!)

您正在使用散列解引用 (%{ ... }),但您没有散列,更不用说对可以解引用的散列的引用。要构建散列并返回对散列的引用,请使用{ ... }

each %{ { apple  => "red", orange => "orange", grape  => "purple" } }

虽然这解决了一个问题,但只是揭示了另一个问题:你得到一个无限循环。

eachkeysvalues 使用的迭代器与哈希关联,而不是运算符。由于每次循环都会创建一个新的哈希,因此每次循环都会创建一个新的迭代器,因此您将始终获得新创建的哈希的第一个元素,并且您的循环永远不会结束。

由于您不需要按键查找项目,我完全不明白您为什么要使用哈希。您可以改用以下内容:

for (
   [ apple  => "red"    ],
   [ orange => "orange" ],
   [ grape  => "purple" ],
) {
   my ($key, $val) = @$_;
   ...
}

如果你从 sub 获得列表,以下是上面的写法。

use List::Util qw( pairs );

for (pairs(f())) {
   my ($key, $val) = @$_;
   ...
}

不过,它们都创建了许多数组。由于破坏性没有问题,因此我将使用以下方法来避免该问题:

{
   my @kvs = f();
   while ( my ($key, $val) = splice(@kvs, 0, 2) ) {
      ...
   }
}

您也可以使用以下内容,但我认为很多人会对此感到困惑:

for (
   my @kvs = f();
   my ($key, $val) = splice(@kvs, 0, 2);
) {
   ...
}

【讨论】:

    【解决方案2】:

    这是无法做到的,因为each 只能与实际的变量、散列或数组一起按预期工作。请参阅文档中的概要。 keysvalues 也是如此。

    第二次尝试也是一样,每次迭代都会重新调用函数。

    请注意,一个明智的尝试是%{ {...} }(而不是%{ (...) }),因为%{} 中的东西必须是一个哈希引用。这适用于这两种尝试,因为函数返回一个散列,由此您将返回一个标量列表。 (根据第一条语句,这仍然无济于事。)


    我不确定这有什么需要,因为可以在循环之前定义散列。另外,我建议在使用之前仔细查看each,因为它具有复杂性。

    我认为您想要迭代动态创建的此类对列表的键值对。这是一种使用自定义迭代器的方法(它包装了each 使用的哈希迭代器)

    use warnings;
    use strict;
    use feature 'say';
    
    my $hit = get_each_it(a => 1, b => 2, c => 3);
    
    while (my ($k, $v) = $hit->()) {
        say "$k => $v";
    }
    
    my ($k, $v) = $hit->();  # restarts the iterator
    say "$k --> $v";
    ($k, $v) = $hit->();     # next (keeps state)
    say "$k --> $v";
    
    sub get_each_it {
        my %h = @_;
        return sub { return each %h }
    }
    

    重复和持续的迭代(在哈希用尽或单独调用之后)是each 使用的哈希迭代器的基本属性,并且这样做

    只要给定的哈希值未修改,您就可以依靠keysvalueseach 重复返回彼此相同的顺序。

    请仔细研究它是如何工作的。

    请参阅 this article on Perl.com 关于迭代器的信息,并附上一些示例。 Iterator 模块中给出了迭代器的详细讨论以及教程。我不太了解该模块,但文档值得一读;每个警告和警告都适用于each


    如果您不需要(或不希望)在哈希用完后重置迭代器以继续迭代,这里是 ikegami 的评论中使用 splice 的替代迭代器

    sub get_each_it {
        my @kv = @_;
        return sub { return splice @kv, 0, 2 }
    }
    

    这不会与each 纠缠在一起,它还会按照提交列表的顺序进行迭代。

    请注意,通过 closure 的属性,生成器返回的每个代码引用仍保留其自己的迭代器,当从各种代码段调用时,该迭代器保持其状态。小心使用。

    另请参阅closure in perlfaq7 上的介绍性说明

    【讨论】:

    • 有没有办法定义一个内联变量,然后将它与每个变量一起使用,也许使用do {} 或类似的东西?
    • @GorkemYurtseven 我不明白这怎么可能,因为它每次都会运行,重新启动迭代器。请参阅 ikegami 的回答,了解您的代码的作用和选项的完整说明。
    • @GorkemYurtseven 我必须问,为什么?不是代码需要这个(为while 动态生成数据),对吧?您可以在此处定义散列,或者如果您不想让它四处浮动,请将其全部放在一个子中。也许返回一个迭代器,结果也会更好。
    • 替代迭代器:sub get_each_it { my @a = @_; sub { splice @a, 0, 2 } }each 不同,它不会重置,但这应该不是问题。我在写我的答案时考虑过提到一个自定义迭代器,但我最终没有包括它,因为它根本对 OP 没有帮助;它仍然需要一个循环范围的变量。
    • @ikegami 谢谢,在不需要或不需要重置时添加到我的答案中(因为它避免了each)。至于目的,我可以在问题中看到的一个合理目标是从动态生成的列表中迭代键值对(不在该范围内添加变量)。将此声明添加到答案中。这就是为什么我决定添加迭代器(其中变量隐藏在子中)。此外,由于 OP 使用each,我认为表明哈希迭代器是可重置的很有用。
    【解决方案3】:

    您可能想要做的事情是使用for 循环。 (不是这样)匿名哈希进入初始化表达式。测试表达式为each的赋值,迭代表达式为空。

    #!/usr/bin/perl
    use strict;
    use warnings;
    
    for (my %h = (
                  apple => 'red',
                  orange => 'orange',
                  grape => 'purple',
                 );
         my ($key, $value) = each(%h);
        ) {
      print("$key: $value\n");
    }
    

    这实际上可以看作是一种while 循环,其初始化是循环本地的。 %h 仅在循环范围内。所以它对for 循环不是匿名的,可以与each 一起使用,但在循环完成后不再存在。

    【讨论】:

    • 没有理由使用哈希。可能更快:for (my @h = ( ... ); my ($key, $value) = splice(@h, 0, 2); ) { ... }
    猜你喜欢
    • 2014-11-14
    • 1970-01-01
    • 2012-09-10
    • 2016-08-20
    • 2020-06-22
    • 2015-06-10
    • 2017-08-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多