Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
是perl';每个函数都值得使用吗?_Perl_Hash_Iterator_Each - Fatal编程技术网

是perl';每个函数都值得使用吗?

是perl';每个函数都值得使用吗?,perl,hash,iterator,each,Perl,Hash,Iterator,Each,我们从中读到: 每个散列都有一个迭代器,由程序中的所有每个、键和值函数调用共享;它可以通过读取散列中的所有元素,或通过计算键散列或值散列来重置 离开包含each()的作用域时,迭代器不会重置,这可能会导致错误: my %h = map { $_, 1 } qw(1 2 3); while (my $k = each %h) { print "1: $k\n"; last } while (my $k = each %h) { print "2: $k\n" } 输出: 1: 1 2

我们从中读到:

每个散列都有一个迭代器,由程序中的所有
每个
函数调用共享;它可以通过读取散列中的所有元素,或通过计算
键散列
值散列
来重置

离开包含
each()
的作用域时,迭代器不会重置,这可能会导致错误:

my %h = map { $_, 1 } qw(1 2 3);
while (my $k = each %h) { print "1: $k\n"; last }
while (my $k = each %h) { print "2: $k\n"       }
输出:

1: 1
2: 3
2: 2

这种行为的常见解决方法是什么?一般来说,每种代码都值得使用吗?

我认为只要你意识到这一点,就值得使用。当您在迭代中同时需要键和值时,它是理想的:

while (my ($k,$v) = each %h) {
    say "$k = $v";
}
在您的示例中,可以通过添加
键%h重置迭代器像这样:

my %h = map { $_ => 1 } qw/1 2 3/;
while (my $k = each %h) { print "1: $k\n"; last }
keys %h;  # reset %h
while (my $k = each %h) { print "2: $k\n" }

从Perl 5.12开始,还将允许对数组进行迭代。

使用该函数重置迭代器。有关更多信息,请参见

我发现
每一个
对于这样的习惯用法都非常方便:

my $hashref = some_really_complicated_method_that_builds_a_large_and_deep_structure();
while (my ($key, $value) = each %$hashref)
{
    # code that does stuff with both $key and $value
}
将该代码与以下代码进行对比:

my $hashref = ...same call as above
foreach my $key (keys %$hashref)
{
    my $value = $hashref->{$key};
    # more code here...
}
在第一种情况下,
$key
$value
可立即用于循环体。在第二种情况下,必须首先获取
$value
。此外,
$hashref
的键列表可能非常庞大,这会占用内存。这有时是一个问题<代码>每个
都不会产生此类开销


但是,
每一个
的缺点并不是很明显:如果提前中止循环,哈希的迭代器不会重置。另外(我发现这个问题更严重,甚至更不明显):你不能在这个循环中调用
keys()
values()
或另一个
each()
。这样做将重置迭代器,您将失去在while循环中的位置。while循环将永远持续下去,这肯定是一个严重的错误。

每一个都不仅值得使用,而且如果你想循环一个对内存来说太大的绑定哈希,它几乎是强制性的


在开始循环之前,一个空的上下文keys()(或值,但一致性很好)是唯一需要的“解决方法”;您寻找其他解决方法是有原因的吗?

最好将其命名为:
每一个
。如果你的意思是“给我第一个键值对”,或者“给我前两个键值对”或者其他什么,那么使用它可能是错误的。请记住,这个想法非常灵活,每次调用它时,都会得到下一对(或标量上下文中的键)

每种
都太危险了,无法使用,许多样式指南完全禁止使用。危险在于,如果在散列结束之前中止每个
的循环,则下一个循环将从那里开始。这会导致很难复制错误;程序的一部分的行为将取决于程序中完全不相关的另一部分。您可能会使用
每一个
对,但是您编写的每一个可能使用您的hash(或hashref;它是相同的)的模块呢


总是安全的,所以只需使用它们即可<代码>键
使以确定性顺序遍历哈希变得更容易,这几乎总是更有用。(
对于我的$key(排序键%hash){…}

每个
都有一个内置的隐藏全局变量,它可能会伤害您。除非您需要这种行为,否则只使用
更安全

考虑一下这个例子,我们希望对k/v对进行分组(是的,我知道
printf
会做得更好):

在实际应用程序中调试上述测试中显示的错误将是非常痛苦的。(为了更好地输出,请使用
Test::Differences
eq\u或_diff
而不是
is

当然,
one()
可以通过使用
键来清除子例程开始和结束处的迭代器来修复。如果你还记得的话。如果你所有的同事都记得的话。只要没有人忘记,它是绝对安全的


我不知道你的情况,但我会坚持使用

如果你在一个绑定的散列中迭代,例如一个包含数百万个键的数据库,那么每个()都会更有效;这样,您就不必加载内存中的所有键。

我想常见的解决方法包括计算
键哈希值
值哈希值
非常好!这是我能想到的使用
每一个
的最好(唯一?)理由。使用很多全局哈希,是吗?不管它是全局还是非全局。即使是类的私有属性也容易出现此问题。任何返回对散列的引用的操作都会受到影响。
#!perl

use strict;
use warnings;

use Test::More 'no_plan';

{   my %foo = map { ($_) x 2 } (1..15);

    is( one( \%foo ), one( \%foo ), 'Calling one twice works with 15 keys' );
    is( two( \%foo ), two( \%foo ), 'Calling two twice works with 15 keys' );
}

{   my %foo = map { ($_) x 2 } (1..105);

    is( one( \%foo ), one( \%foo ), 'Calling one twice works with 105 keys' );
    is( two( \%foo ), two( \%foo ), 'Calling two twice works with 105 keys' );
}


sub one {
    my $foo = shift;

    my $r = '';

    for( 1..9 ) {
        last unless my ($k, $v) = each %$foo;

        $r .= "  $_: $k -> $v\n";
    }
    for( 10..99 ) {
        last unless my ($k, $v) = each %$foo;

        $r .= " $_: $k -> $v\n";
    }

    return $r;
}

sub two {
    my $foo = shift;

    my $r = '';

    my @k = keys %$foo;

    for( 1..9 ) {
        last unless @k;
        my $k = shift @k;

        $r .= "  $_: $k -> $foo->{$k}\n";
    }
    for( 10..99 ) {
        last unless @k;
        my $k = shift @k;

        $r .= "  $_: $k -> $foo->{$k}\n";
    }

    return $r;
}