Как разбить значения массива на новые отдельные массивы в Perl? - PullRequest
3 голосов
/ 09 мая 2019

Я новичок в изучении 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 = ААА

разделенный2 = 111

разделенный3 = AAA

Желаемый результат;

separa1 = aaa bbb ccc ddd

разделено2 = 111 222 333 444

разделенный3 = AAA BB CCC DD

Ответы [ 6 ]

4 голосов
/ 09 мая 2019

Редкий случай, когда подходит цикл в стиле C для итерации по каждому третьему элементу

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 .


И еще есть много модулей со всеми видами утилит для работы со списками.

Например, используя part из List :: MoreUtils , чтобы разбить массив @values

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

Это выдает тот же @sep с массивами, как указано выше.

part возвращает список arrayrefs, каждый из которых содержит индексы, поскольку он разделил список индексов @values. Затем в map каждый массив ссылается на свой список индексов (@$_), который используется для взятия соответствующего среза @values; этот список используется для создания массива с []. Так что map возвращает список arrayrefs со значениями, разделенными по мере необходимости.

Для работы со ссылками см. Учебник perlreftut и справку perlref

4 голосов
/ 09 мая 2019
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;
}
2 голосов
/ 09 мая 2019

Другая работа для Список :: 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";
2 голосов
/ 09 мая 2019

Другая версия, использующая 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

Волшебство здесь в том, что состояние , которое в итоге создает локальную переменную для блока, в котором она хранится, и сохраняет его значение в нескольких оценках блока.

1 голос
/ 10 мая 2019

Мне нравятся решения от @ikegami и @zdim. Использование @ zdim part() из List::MoreUtils напомнило мне о 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() является разрушительным.

1 голос
/ 10 мая 2019

Это выражение может помочь вам получить желаемые результаты:

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

Он имеет три группы захвата для каждого из ваших желаемых результатов. Вы можете добавлять или уменьшать границы по своему усмотрению, и это может быть быстрее, чем другие методы.

enter image description here

Graph

Этот график показывает, как будет работать выражение, и вы можете визуализировать другие выражения в этой ссылке :

enter image description here

JavaScript Test

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 Test

Вы можете просто использовать $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 показывает производительность этого выражения, используя простой цикл 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. ? ");
...