【问题标题】:How to sum two lists element-wise如何按元素对两个列表求和
【发布时间】:2012-05-23 09:11:43
【问题描述】:

我想逐行解析一个文件,每个文件都包含两个整数,然后将这些值相加到两个不同的变量中。我的幼稚做法是这样的:

my $i = 0;
my $j = 0;
foreach my $line (<INFILE>)
{
    ($i, $j) += ($line =~ /(\d+)\t(\d+)/);
}

但它会产生以下警告:

在 void 上下文中对私有变量的无用使用

暗示诉诸 += 运算符会触发以标量而不是列表上下文对左侧进行评估(如果我在这一点上错了,请纠正我)。

是否可以在不借助数组或中间变量的情况下优雅地(可能在一行中)实现这一点?


相关问题:How can I sum arrays element-wise in Perl?

【问题讨论】:

    标签: perl list


    【解决方案1】:

    不,这是因为表达式 ($i, $j) += (something, 1) 解析为仅将 1 添加到 $j,而使 $i 挂在无效上下文中。 Perl 5 没有超级运算符或赋值运算符(例如+=)的自动压缩。这有效:

    my ($i, $j) = (0, 0);
    foreach my $line (<INFILE>) {
        my ($this_i, $this_j) = split /\t/, $line;
        $i += $this_i;
        $j += $this_j;
    }
    

    您可以通过使用复合数据结构而不是列的命名变量来避免重复。

    【讨论】:

    • 你最后一句话是什么意思?例如,一个二元素数组仍然需要单独的算术语句,除非您正在考虑重载 +=?
    • 就像 kratenko 回答中的数组一样。这 - 再加上无限制的拆分 - 适用于任意数量的列。
    【解决方案2】:

    首先,您成对添加数组的方式不起作用(您自己发布的相关问题在那里给出了一些提示)。

    对于解析部分:拆分行怎么样?如果您的行被相应地格式化(空格应该不是问题)。

    split(/\t/, $line, 2)
    

    如果你真的,真的想在一行中完成,你可以做这样的事情(虽然我不认为你会称之为优雅):

    my @a = (0, 0);
    foreach my $line (<INFILE>)
    {
        @a = map { shift(@a)+$_ } split(/\t/, $line, 2);
    }
    

    对于@lines = ("11\t1\n", " 22 \t 2 \n", "33\t3"); 的输入,它给了我@a = (6, 66)

    我建议您使用我的答案的拆分部分,但不要使用加法部分。使用多条线没有错!如果它使您的意图更清晰,那么多行总比一行好。但是现在我几乎不再使用 perl,而是使用 python,所以我的 perl 编码风格可能会在那里产生“不好”的影响......

    【讨论】:

    • map 表达式是完美的 Perl,没有不清楚的意图。 +1
    • @daxim Hej,所以我今天学了一个新词,cromulent。感谢您丰富我的词汇量。
    • split 调用中 2 的限制假定每条记录只有两个字段。否则你会得到(field1, rest-of-record)。即使只有两个字段,它也几乎毫无意义,并且还会在第二个字段的末尾留下换行符。
    • @borodin 最后的换行符应该不是问题(当我尝试时工作)。但好点在那里,应该可以使它适用于任何(恒定)数量的列。等我回到自己的电脑前,我会试试的。
    • +1 表示分割部分。我完全同意你的“如果它让你的意图更清晰,更多的行比一个更好。
    【解决方案3】:

    每次添加都可以交换对,这意味着您总是添加到每对中的相同元素。 (如果需要,这可以推广到旋转多元素数组。)

    use strict;
    use warnings;
    
    my @pair = (0, 0);
    
    while (<DATA>) {
      @pair = ($pair[1], $pair[0] + $_) for /\d+/g;
    }
    
    print "@pair\n";
    
    __DATA__
    99 42
    12 15
    18 14
    

    输出

    129 71
    

    【讨论】:

      【解决方案4】:

      这是另一个选择:

      use Modern::Perl;
      
      my $i = my $j = 0;
      
      map{$i += $_->[0]; $j += $_->[1]} [split] for <DATA>;
      
      say "$i - $j";
      
      __DATA__
      1   2
      3   4
      5   6
      7   8
      

      输出:

      16 - 20
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-01-30
        • 1970-01-01
        • 1970-01-01
        • 2020-07-19
        • 2023-04-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多