【问题标题】:How to do two different Foreach loops without having to duplicate code如何在不重复代码的情况下执行两个不同的 Foreach 循环
【发布时间】:2013-09-03 11:58:36
【问题描述】:

我有一个数组散列和一个普通数组,根据情况(即用户在程序运行时选择的选项),只会定义其中一个。

演示问题的示例代码:

my %hashofarrays;
my @array;

#...
#Some code between here where either %hashofarrays or @array gets defined
#...

if (defined @array) {

    foreach my $var1 (@array) {
        print "var1 is: $var1\n";
        call_subroutine($var1);
        print "Something else is printed";
        call_anothersubroutine($var1);
        call_differentsubroutine($var1);
    }

} 

if (defined %hashofarrays) {

    foreach my $key (keys %hashofarrays) {
        print "the key is: $key\n";
        foreach my $var1 (@{$hashofarrays{$key}}) {
            call_subroutine($var1);
            print "Something else is printed";
            call_anothersubroutine($var1);
            call_differentsubroutine($var1);
        }
    }
}
   

正如您在上面的代码中看到的,取决于是否定义了@array 或是否定义了%hashofarrays,它将运行相应的if 语句。

问题:

但问题在于以下代码行在两个 if 语句中重复:

    call_subroutine($var1);
    print "Something else is printed";
    call_anothersubroutine($var1);
    call_differentsubroutine($var1);

显然,如果这些foreach 循环包含大量代码,则意味着大量代码将被重复。

当涉及到foreach 循环时,有什么方法/最好的方法是避免这种代码重复?

实际上,有没有办法执行以下操作:(我很清楚这段代码不起作用,但解释了我想要实现的目标)

if (defined @array) {
    foreach my $var1 (@array) {
} elsif (defined %hashofarrays) {
    foreach my $key (keys %hashofarrays) {
        print "the key is: $key\n";
        foreach my $var1 (@{$hashofarrays{$key}}) {

} #ending bracket of if statement

        call_subroutine($var1);
        print "Something else is printed";
        call_anothersubroutine($var1);
        call_differentsubroutine($var1); 

}  #ending bracket of whatever foreach loop is used

我很可能在这里忽略了一些显而易见的事情,但看不到这样做的合乎逻辑的方法?

【问题讨论】:

标签: perl foreach code-duplication


【解决方案1】:

首先,defined @arraydefined %hashofarrays错误的。它们总是被定义的。您希望if (@array)if (keys %hashofarrays) 测试它们是否包含 元素。你甚至应该收到警告defined(@array) is deprecated

你想要的是另一个子程序。

sub loop_body {  # just use some better name!
  my ($var) = @_;
  call_subroutine($var);
  print "Something else is printed";
  call_anothersubroutine($var);
  call_differentsubroutine($var);
}

然后:

if (@array) {
    foreach my $var1 (@array) {
        print "var1 is: $var1\n";
        loop_body($var1);
    }
} elsif (keys %hashofarrays) {
    foreach my $key (keys %hashofarrays) {
        print "the key is: $key\n";
        foreach my $var1 (@{$hashofarrays{$key}}) {
            loop_body($var1)
        }
    }
}

您还可以使用回调使您的解决方案更加灵活:

 sub loop_with_cb {
   my $cb = shift;
   for my $var (@_) {
     if ($cb) {
       local $_ = $var; # make $_ visible to the callback
       $cb->($var);
     }
     call_subroutine($var);
     print "Something else is printed";
     call_anothersubroutine($var);
     call_differentsubroutine($var);
   }
 }

然后:

if (@array) {
    loop_with_cb(
      sub { print "var1 is: $_\n" },
      @array,
    );
} elsif (keys %hashofarrays) {
    foreach my $key (keys %hashofarrays) {
        print "the key is: $key\n";
        loop_with_cb(undef, @{ $hashofarrays{$key} });
    }
}

【讨论】:

  • 也许是for (@_){ my $var = $_; } 而不是local $_ = $var;
  • @mpapec 我考虑过这一点,但我认为尽可能使用显式的词法循环变量很重要。明确的local 也是一种强有力的表达方式,即“我希望让我呼叫的潜艇看到它”。考虑到 OP 对 Perl 的了解,应避免隐式行为。
  • 我前段时间对$_ 的手动本地化感到不满,但不记得为什么应该避免它。我会尽力找到那篇文章。
  • @mpapec 我会对这样的文章感兴趣。但是你确定你没有将它与词汇$_ 混淆吗?如今,my $_ 与 5.10 中的其他错误一起被劝阻。
【解决方案2】:

使用其他功能可能会有所帮助,

if (@array) {
    my_func(\@array);
} 
if (%hashofarrays) {
    foreach my $key (keys %hashofarrays) {
        print "the key is: $key\n";
        my_func($hashofarrays{$key});
    }
}

sub my_func {
    my ($arr) = @_;

    foreach my $var1 (@$arr) {
        call_subroutine($var1);
        print "Something else is printed";
        call_anothersubroutine($var1);
        call_differentsubroutine($var1);
    }

}

【讨论】:

    【解决方案3】:

    正如 amon 所说,if (defined @array) 是错误的,但我什至不会打扰 if (@array),除非您将拥有一个 else。您可以直接转到for (@array),如果@array 为空,它将迭代零次,基本上就像if 一样跳过循环。

    除此之外,您似乎想对@array 中的每个元素和%hash of arrays 中的每个键执行相同的操作,对吧?你甚至不需要sub

    for (@array, map { @$_ } values %hashofarrays) {
      my $var1 = $_;
      print "var is: $var1\n";
      call_subroutine($var1);
      print "Something else is printed";
      call_anothersubroutine($var1);
      call_differentsubroutine($var1);
    }
    

    编辑: 修改后的代码循环遍历 %hashofarrays 中数组的内容,而不是其键。这仍然与初始问题中的代码不完全匹配,因为它在循环遍历 hoa 时打印 $var1 而不是打印哈希键,但看起来 OP 的真实用例很有可能不会除了索引哈希之外,不要将密钥用于任何其他事情,在这种情况下,无论如何都不需要它(因为我们可以使用 values %hoa 代替)。

    【讨论】:

    • 那行不通,因为$_ 表示数组中字段的内容,但在哈希中它只是键,而不是值。据我所知,哈希特定的for循环中需要哈希中的键值。而且由于哈希是用数组引用填充的,即使有某种方法可以使 $_ 成为哈希条目的值,这也不起作用。
    • @Vince:啊,好接球!我将修改我的答案以循环 @array%hashofarrays 中的数组内容。
    猜你喜欢
    • 2022-12-14
    • 1970-01-01
    • 2020-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多