【问题标题】:How can I iterate over an array with foreach without changing the address of the loop variable?如何在不更改循环变量地址的情况下使用 foreach 迭代数组?
【发布时间】:2012-11-08 21:08:23
【问题描述】:

当我运行这样一段简单的代码时:

my @arr=(1..5);
my $x;
foreach $x (@arr) {
    $x+=10;
}
print "@arr";

结果是“11 12 13 14 15”,因为 $x “变成”了 foreach 中 @arr 数组中的每个元素。够了。

但这是我的事...不是问题(解决方案很简单,但不优雅,我希望我的 perl 尽可能优雅)。

我写了一个 tie 模块来处理 COBOL 数据。它需要一个副本,解析字段,然后将其附加到一个标量/字符串,以便访问/从绑定的哈希将返回/设置字符串中的值。效果很好。

my %h,$rec;
my $cb=<<END;
       01 CH-RECORD.
          05 JOB-NUM PIC X.
          05 FILLER  PIC X(76).
          05 REC-TYPE PIC X(2).
END
tie %h, 'COBOLDataTie',$cb,\$rec; #tie the hash to the record via the copybook

从那里,我可以将 COBOL 记录移动到 $rec 并使用 %h 哈希访问 COBOL 字段。

同样,这非常有效。但是当我想迭代一个 COBOL 记录数组时,问题就来了。所以如果在上面的代码之后我有类似的东西:

foreach $rec (@arr) {
    print "Job is ",$h{'JOB-NUM'},"\n";
}

它不起作用,因为 foreach 实际上改变了 $rec 的位置,这打破了它的关系。我最终不得不做这样的事情:

foreach (@arr) {
    $rec=$_;
    print "Job is ",$h{'JOB-NUM'},"\n";
}

有什么方法可以让我执行“foreach $rec (@arr)”而不破坏我绑定的哈希?

(在任何人说之前,是的,我知道这需要一个很好的面向对象的解决方案……总有一天我会做到的;我只需要先找点时间)

结语:我将 TieHash 代码修改为,它不是指向外部记录,而是截取哈希的“特殊”键,其中是“记录”。因此,当我将记录字符串分配给 $h{'record'} 时,它与在上面的示例中加载 $rec 相同。这是一个更好的解决方案,更独立。它还公开了一个更像 OOP 的接口。

【问题讨论】:

  • @ikegame 我想我明白 OP 的意思了。查看第二个代码块。他正在将$rec 绑定到哈希......不知何故。
  • 您为什么使用基于tie 的模块?它比只使用一个对象更好吗?
  • 正如我所指出的,我还没有完成 OOP 版本……如果我的工作量减轻的话,也许有一天。 @simbabque,我实际上是在绑定 \$rec,而不是 $rec。
  • 实际上是一个基于tie的模块面向对象的。请注意,在Tie::Hash 中提到了作为“绑定哈希的基础”。

标签: perl


【解决方案1】:

您决定创建的接口是“分配给$rec,然后通过%h 访问字段”。因此,这正是您需要做的。

for (@arr) {
    $rec = $_;
    print "Job is $h{'JOB-NUM'}\n";
}

当然它看起来很奇怪,但那是因为它很奇怪。这样会更合理:

for (@arr) {
    my $h = parse($cb, $_);
    print "Job is $h->{'JOB-NUM'}\n";
}

您甚至可以通过最少的更改来做到这一点:

sub parse {
    my ($cb, $rec) = @_;
    tie my %h, 'COBOLDataTie', $cb, \$rec; 
    return \%h;
}

【讨论】:

  • 您的建议的缺点是每条记录都必须每次都解析每个字段。在具有数百个字段的记录中,每个字段都必须从 COBOL 格式解构为常规标量(可能包括 EBCDIC 到 ASCII 的转换),如果我只需要记录中的 1 或 2 个字段,则此方法将大大减慢处理时间。使用绑定哈希允许代码仅在使用字段时转换字段,而无需转换所有字段。我之前在这方面的努力确实具有您所建议的解析功能。
  • 哎呀,是的,它不等价。
  • 一个更合理的绑定数组 API 可能是tie my %h, 'COBOLDataTie', $cb; $h{$rec}{'JOB-NUM'}$h{rec} 需要返回一个指向绑定哈希的引用。
  • 我已经尝试了上面的第三个示例...每次读取新记录时绑定一个变量会大大减慢速度。我还考虑了您上面评论中使用嵌套哈希的技术......但后来我的大脑开始受伤:-) 最后,这真的需要一个全面的 OOP 解决方案。但即便如此,事情也是有限的。曾经尝试过“foreach $h{'rec'} (@arr)”吗?编译器轻笑一声,然后质疑你的出身!
【解决方案2】:

这是一个微妙的点,在文档中很容易被忽略,但 foreach 变量中的循环变量始终是一个新变量,与任何其他地方的同名词法或包变量无关程序。

来自perlsyn

Foreach 循环

“foreach”循环遍历一个普通的列表值并设置 变量 VAR 依次成为列表的每个元素。如果变量 以关键字“my”开头,然后它是词法范围的,并且是 因此仅在循环内可见。否则,变量为 隐式本地到循环并在退出时恢复其以前的值 循环。如果变量之前用“my”声明过,它使用 该变量而不是全局变量,但它仍然本地化为 循环。这种隐式本地化只发生在“foreach”循环中。

(强调添加)。即这个小脚本第3行的$rec与第1行声明的$rec无关

1: my $rec = 'foo';
2: print $rec;                       # 'foo'
3: foreach $rec (@some_list) {
4:     print $rec;                   # something else
5: }
6: print $rec;                       # 'foo' again

因此,如果您想使用 \$rec 来影响绑定哈希的行为(尽管肯定有其他方法可以做到这一点),那么您正在做正确的事情,使用不同的循环变量并将 $rec 分配给它在循环中。

【讨论】:

  • 非常好的信息,谢谢。不幸的是,没有回答我的问题,但只会强调我不能按照我想要的方式去做。
【解决方案3】:

似乎最好的方法是:

for (my $i=0;($rec=$arr[$i], $i<@arr);$i++) {

不完全是我所希望的优雅,但它似乎有效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-08-02
    • 2020-02-26
    • 1970-01-01
    • 1970-01-01
    • 2013-03-29
    • 1970-01-01
    • 1970-01-01
    • 2012-03-30
    相关资源
    最近更新 更多