【问题标题】:How to split array values into to new separate arrays in Perl?如何在 Perl 中将数组值拆分为新的单独数组?
【发布时间】:2019-05-09 06:23:44
【问题描述】:

我是学习 perl 的初学者。 我在这里要做的是拆分数组@value 并将其插入到一个新数组中。我的问题是,我不知道如何让我的编码循环运行并获得我想要的结果。

是否有可能使用此方法获得所需的结果,或者是否有任何其他替代方法/方法可以获得相同的结果?

我的代码如下;

my @separated = ();
my @separated1 = ();
my @separated2 = ();
my @separated3 = ();
my $counter = 0;
my @values = "aaa 111 AAA bbb 222 BBB ccc 333 CCC ddd 444 DDD";

foreach (@values) {
my @separated = split(' ', $_);
push @separated1, $separated[0];
push @separated2, $separated[1];
push @separated3, $separated[2];
}
$counter++

print "separated1 = @separated1\n";
print "separated2 = @separated2\n";
print "separated3 = @separated3\n";

我得到的结果;

分隔1 = aaa

分隔2 = 111

分隔3 = AAA

期望的结果;

separated1 = aaa bbb ccc ddd

分隔2 = 111 222 333 444

separated3 = AAA BB CCC DD

【问题讨论】:

  • 实际上你不使用你的 $counter 变量。你增加它,但永远不要访问它。我猜你想用它作为你的数组的索引。
  • 这一行:my @values = "aaa 111 AAA bbb 222 BBB ccc 333 CCC ddd 444 DDD" ... 将一个标量值分配给一个数组。它有效地将标量值放入@value 数组的第一个槽中。你可以直接在这里做一个splitmy @values = split / /, "aaa 111 AAA bbb 222 BBB ccc 333 CCC ddd 444 DDD"; 一旦这个到位,剩下的就是一次取3个值等等。

标签: arrays regex perl split push


【解决方案1】:

C 风格 for 循环适用的罕见情况,迭代每 3 个元素

my $string = 'aaa 111 AAA bbb 222 BBB ccc 333 CCC ddd 444 DDD';

my (@sep1, @sep2, @sep3);

my @values = split ' ', $string;

for (my $i=0; $i <= $#values; $i += 3) {
    push @sep1, $values[$i];
    push @sep2, $values[$i+1];
    push @sep3, $values[$i+2];
}

这假设数组确实包含所有三元组,或者更好地检查每个元素。

但是使用单个结构通常比使用一组并行数组要好得多。例如,使用带有数组引用元素的数组

use Data::Dump qw(dd);

my @sep;

for (my $i=0; $i <= $#values; $i += 3) { 
    for my $j (0..2) { 
        push @{$sep[$j]}, $values[$i+$j]; 
    }
}

dd \@sep;

使用更清洁的方法可以避免双重迭代

for my $i (0..$#values) { 
    push @{$sep[$i%3]}, $values[$i] 
}

替换了两个循环。

打印出来

[ [“aaa”,“bbb”,“ccc”,“ddd”], [111、222、333、444]、 [“AAA”,“BBB”,“CCC”,“DDD”], ]

我使用Data::Dump 来查看复杂数据。核心中的替代方案是Data::Dumper


还有许多模块,其中包含用于处理列表的各种实用程序。

例如,使用List::MoreUtils 中的part@values 数组进行分区

my @sep = map { [ @values[@$_] ] } part { $_%3 } 0..$#values;

这会产生与上面相同的 @sep 和 arrayrefs。

part 返回一个数组引用列表,每个引用都包含索引,因为它对@values 的索引列表进行了分区。然后在map 中,每个arrayref 都被评估为其索引列表(@$_),用于获取@values 的相应切片;该列表用于创建带有[] 的arrayref。所以map 返回一个数组引用列表,其中的值根据需要进行分区。

有关使用参考资料,请参阅教程perlreftut 和参考资料perlref

【讨论】:

    【解决方案2】:
    my ( @foos, @bars, @quxs );
    my @values = split(' ', $input);
    while (@values) {
       push @foos, shift(@values);
       push @bars, shift(@values);
       push @quxs, shift(@values);
    }
    

    上面也可以写成:

    my ( @foos, @bars, @quxs );
    for ( my @values = split(' ', $input); @values; ) {
       push @foos, shift(@values);
       push @bars, shift(@values);
       push @quxs, shift(@values);
    }
    

    您确定要使用并行数组吗?虽然它们可以节省内存,但通常很难使用它们,而且更容易出错。在以物体为主的环境中,它们几乎从未见过。

    您可以使用 AoA:

    my @moos;
    my @values = split(' ', $input);
    while (@values) {
       push @moos, [ splice(@values, 0, 3) ];
    }
    

    您可以使用 AoH:

    my @moos;
    my @values = split(' ', $input);
    while (@values) {
       my %moo; @moo{qw( foo bar qux )} = splice(@values, 0, 3);
       push @moos, \%moo;
    }
    

    【讨论】:

    • 如果原始$values中的项目数不能被3整除,第一个解决方案会将未定义的值放入结果中。
    • @Silvar,这是故意的,所以最好指出 AoA 解决方案没有这样做。
    【解决方案3】:

    另一个使用part 的版本来自非核心但非常有用的List::MoreUtils 模块,它直接对元素进行分区:

    #!/usr/bin/perl
    use warnings;
    use strict;
    use feature qw/say state/;
    use List::MoreUtils qw/part/;
    
    my $str = "aaa 111 AAA bbb 222 BBB ccc 333 CCC ddd 444 DDD";
    
    my ($sep1, $sep2, $sep3) = part { state $i = 0; $i++ % 3 } split(' ', $str);
    
    say "sep1: @$sep1";
    say "sep2: @$sep2";
    say "sep3: @$sep3";
    

    打印出来

    sep1: aaa bbb ccc ddd
    sep2: 111 222 333 444
    sep3: AAA BBB CCC DDD
    

    这里的魔力在于state,它最终创建了一个对其所在块的本地变量,该变量在对块的多次评估中保留其值。

    【讨论】:

      【解决方案4】:

      List::UtilsBy 的另一份工作:

      use strict;
      use warnings;
      use List::UtilsBy 'bundle_by', 'unzip_by';
      
      my $string = 'aaa 111 AAA bbb 222 BBB ccc 333 CCC ddd 444 DDD';
      my @vals = split ' ', $string;
      my ($sep1, $sep2, $sep3) = unzip_by { @$_ } bundle_by { [@_] } 3, @vals;
      
      print "sep1: @$sep1\nsep2: @$sep2\nsep3: @$sep3\n";
      

      【讨论】:

        【解决方案5】:

        This expression 可能会帮助您获得想要的结果:

        ([a-z]+\s)([0-9]+\s)([A-Z]+)
        

        它为您想要的每个结果提供三个捕获组。您可以根据需要为其添加或减少边界,并且它可能比其他方法更快。

        图表

        此图显示了表达式的工作原理,您可以在此 link 中可视化其他表达式:

        JavaScript 测试

        const regex = /([a-z]+\s)([0-9]+\s)([A-Z]+)/gm;
        const str = `aaa 111 AAA bbb 222 BBB ccc 333 CCC ddd 444 DDD`;
        const subst = `\n$1 & $2 & $3\n`;
        
        // The substituted value will be contained in the result variable
        const result = str.replace(regex, subst);
        
        console.log('Substitution result: ', result);

        Perl 测试

        您可以简单地使用$1$2$3 并分离您的数据:

        use strict;
        
        my $str = 'aaa 111 AAA bbb 222 BBB ccc 333 CCC ddd 444 DDD';
        my $regex = qr/([a-z]+\s)([0-9]+\s)([A-Z]+)/mp;
        my $subst = '';
        
        my $result = $str =~ s/$regex/$subst/rg;
        
        print "The result of the substitution is' $result\n";
        

        性能测试

        此 JavaScript sn-p 使用简单的 100 万次 for 循环显示了该表达式的性能。

        const repeat = 1000000;
        const start = Date.now();
        
        for (var i = repeat; i >= 0; i--) {
        	const string = 'aaa 111 AAA bbb 222 BBB ccc 333 CCC ddd 444 DDD';
        	const regex = /([a-z]+\s)([0-9]+\s)([A-Z]+)/gm;
        	var match = string.replace(regex, "$1");
        }
        
        const end = Date.now() - start;
        console.log("YAAAY! \"" + match + "\" is a match ??? ");
        console.log(end / 1000 + " is the runtime of " + repeat + " times benchmark test. ? ");

        【讨论】:

          【解决方案6】:

          我喜欢@ikegami 和@zdim 的解决方案。 @zdim 从List::MoreUtils 中使用part() 让我想起了natatime

          my @values = split(' ', "aaa 111 AAA bbb 222 BBB ccc 333 CCC ddd 444 DDD"); 
          use List::MoreUtils 'natatime';              
          my $nata_iter = natatime 3, @values ;
          my @aoa ;           
          while (my @tmp = $nata_iter->()) { push @aoa, \@tmp; };
          

          不是真正需要考虑但可能感兴趣:通过使用临时数组 (@tmp) 来存储迭代器的输出,原始的 @values 保持不变,而更直接的 splice() 具有破坏性。

          【讨论】:

            猜你喜欢
            • 2022-01-14
            • 1970-01-01
            • 2016-04-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-10-29
            相关资源
            最近更新 更多