Для проблем с циклом при создании вложенного массива - PullRequest
0 голосов
/ 02 июля 2019

Создание матрицы продуктов для трехэлементных массивов.Я понимаю, что Perl не имеет многомерных массивов и сплющен.Я использовал refs, но, похоже, я не могу обойти проблему цикла for, заключая три продукта в один массив и помещая этот массив в другой отдельный массив.И я тоже могу быть далеко.Будьте хороши, но я потратил слишком много часов на это.

Я переместил значения внутри и из разных мест, например, {}, распечатал переменные до синего цвета и использовал $ last для отладки.,Я, вероятно, зажарен на этом этапе.

use strict;
use warnings;

my @array1 = (1, 2, 3);
my @array2 = (2, 4, 6);
my @matrixArray = ();
my $matrixArray;
my @row; 

my @finalArray = maths(\@array1, \@array2);
print @finalArray;

sub maths{
    my $array1ref = shift;
    my $array2ref = shift;
    my $value1;
    my $value2;
    my $maths;
    my @row = ();

    my @array1 = @{$array1ref};
    my @array2 = @{$array2ref};
    my $len1 = @array1;
    my $len2 = @array2;

for my $x (0 ..($len1 -1)){
    #iterate through first array at each value
    $value1 = $array1[$x];
        #print $value1, " value1 \n";

    for my $y (0 .. ($len2 -1)){
    #iterate through second array at each value
    $value2 = $array2[$y];
            #print $value2, " value2 \n";

    #calculate new values
    $maths = $value1 * $value2;
            #exactly right here
            #print $maths, " maths \n" ;
            push @row, $maths;
    }
}
#and exactly right here but not set of arrays
#print @row, "\n";
return @row;       
}

В настоящее время я могу получить это: 246481261218. Какая правильная тупая математика, но ...

она должна выглядеть какmatrix:

2  4  6
4  8 12
6 12 18

Я не передаю три массива, поэтому, похоже, моя проблема в подпрограмме, прежде чем я смогу заняться чем-то еще.Кажется, это тема, по которой я часто скучаю.Извините, если я звучу неумело.

РЕДАКТИРОВАТЬ *** Это работало, но я не смог распаковать его

use strict;
use warnings;

my @array1 = (1, 2, 3);
my @array2 = (2, 4, 6);
my @matrixArray = ();

maths(\@array1, \@array2);
foreach my $x (@matrixArray){
    print "$x \n";
}

sub maths{
    my $array1ref = shift;
    my $array2ref = shift;
    my $value1;
    my $value2;
    my $maths;
    my @row = ();
    my $row; 

    my @array1 = @{$array1ref};
    my @array2 = @{$array2ref};
    my $len1 = @array1;
    my $len2 = @array2;

    for my $x (0 ..($len1 -1)){
        #iterate through first array at each value
        $value1 = $array1[$x];

        for my $y (0 .. ($len2 -1)){
        #iterate through second array at each value
        $value2 = $array2[$y];

        #calculate new values
        $maths = $value1 * $value2;
        push @row, $maths;
        $row  = \@row;
    }
        push @matrixArray, $row;
    }
    return @matrixArray;
}

Вывод сразу после вызова функции такой:

ARRAY(0x55bbe2c667b0) 
ARRAY(0x55bbe2c667b0) 
ARRAY(0x55bbe2c667b0) 

, который будет (строка 10) печатать из $ x.

**** РЕДАКТИРОВАТЬ Это работает (почти):

print join(" ", @{$_}), "\n" for @matrixArray;

Вывод немного неправильный...

2 4 6 4 8 12 6 12 18

2 4 6 4 8 12 6 12 18

2 4 6 4 8 12 6 12 18

И примечание: я знал, что $ x - это массив, но, похоже, у меня возникли проблемы при попытке его правильно распаковать.И я больше не фанат Perl.Я тоскую по фьордам Питона.

И ***** РЕДАКТИРОВАТЬ Это прекрасно работает, и я получаю из него три массива:

sub maths{
my ($array1, $array2) = @_;
my @res;
for my $x (@$array1) {
    my @row;
    for my $y (@$array2) {
        push @row, $x * $y;
    }

    push @res, \@row;
}
    #This is the correct structure on print @res!
    return @res;
}

Но, хотя это ставитэто вместе правильно, у меня нет выхода после звонка

maths(\@array1, \@array2);

НИЧЕГО ЗДЕСЬ ...

print @res;
print join(" ", @{$_}), "\n" for @res;

foreach my $x (@res){
    print join(" ", @{$x}), "\n";
}

И конечно миллион спасибо!Я сожалею о том, что выбрал этот дурацкий курс, и боюсь, что моя оценка в конечном итоге поможет мне. Все еще тоскую по Python!

Ответы [ 2 ]

4 голосов
/ 02 июля 2019

Похоже, вам нужна матрица со строками, полученными умножением массива на элементы другого.

В одну сторону

use warnings;
use strict;    
use Data::Dump qw(dd);

my @ary     = (2, 4, 6);
my @factors = (1, 2, 3);

my @matrix = map {
    my $factor = $_;
    [ map { $_ * $factor } @ary ]
} @factors;

dd @matrix;

Массив @matrix, образованный внешним map, имеет ссылки на массивы для каждого элемента и, таким образом, является (по меньшей мере) двумерной структурой («матрицей»).Эти arrayrefs построены с [ ], который создает анонимный массив из списка внутри.Этот список генерируется map поверх @ary.

Я использую Data :: Dump для удобной печати сложных данных.В ядре есть Data::Dumper.


При такой большой работе и больших данных эффективность может иметь значение.По общему мнению, прямая итерация должна быть немного быстрее, чем map, но вот эталонный тест.Это также служит для демонстрации более простых способов.

use warnings;
use strict;
use feature 'say';

use Benchmark qw(cmpthese);    

my $runfor = shift // 5;  # run each case for these many seconds

sub outer_map {
    my ($ary, $fact) = @_; 
    my @matrix = map {
        my $factor = $_; 
        [ map { $_ * $factor } @$ary ]
    } @$fact;
    return \@matrix;
}

sub outer {
    my ($ary, $fact) = @_; 
    my @matrix; 
    foreach my $factor (@$fact) {
        push @matrix, []; 
        foreach my $elem (@$ary) {
            push @{$matrix[-1]}, $elem * $factor;
        }
    }   
    return \@matrix;
}

sub outer_tmp {
    my ($ary, $fact) = @_;
    my @matrix;
    foreach my $factor (@$fact) {
        my @tmp;
        foreach my $elem (@$ary) {
            push @tmp, $elem * $factor;
        }
        push @matrix, \@tmp;
    }
    return \@matrix;
}

my @a1 = map { 2*$_ } 1..1_000;  # worth comparing only for large data
my @f1 = 1..1_000;

cmpthese( -$runfor, {
    direct => sub { my $r1 = outer(\@a1, \@f1) },
    w_tmp  => sub { my $r2 = outer_tmp(\@a1, \@f1) },
    w_map  => sub { my $r3 = outer_map(\@a1, \@f1) },
});

На хорошей машине с v5.16 это печатает

         Rate direct  w_map  w_tmp
direct 11.0/s     --    -3%   -20%
w_map  11.4/s     3%     --   -17%
w_tmp  13.8/s    25%    21%     --

Результаты довольно похожи на v5.29.2, ина старом ноутбуке.

Так что map на ощупь быстрее, чем построение матрицы напрямую, и на 15-20% медленнее, чем метод с использованием временного массива для строк, который я также считаю наиболее понятным.Явные циклы можно немного улучшить, избегая областей и скаляров, а «прямой» метод может быть ускорен с помощью индексов.Но это страшная микрооптимизация, и в лучшем случае она дает дополнительные преимущества.

Обратите внимание, что такие моменты времени имеют смысл только с действительно большими объемами данных, а вышеприведенные - нет.(Я тестировал оба измерения в десять раз больше, с очень похожими результатами.)

2 голосов
/ 02 июля 2019

Вторая программа в основном правильная.

Проблема в том, что вы не распаковали второй уровень массива.

foreach my $x (@matrixArray){
    print "$x \n";
}

должно быть что-то вроде:

foreach my $x (@matrixArray) {
    print join(" ", @{$x}), "\n";
}

# or just:
print join(" ", @{$_}), "\n" for @matrixArray;

Ваша функция maths может быть сокращена без потери разборчивости (на самом деле она может быть более разборчивой) путем удаления ненужных временных переменных и индексации. Например:

sub maths {
    my @array1 = @{ $_[0] };
    my @array2 = @{ $_[1] }; # or: ... = @{ (shift) };
    my @res = ();
    for my $x (@array1) {
        my @row = (); # <-- bugfix of original code
        for my $y (@array2) {
            my $maths = $x * $y;
            push @row, $maths;
        }
        push @res, \@row;
    }
    return @res;
}
...