Доступ к двумерному массиву в Perl

Пытаюсь выучить Perl. У меня есть массив, населенный городами. Я хочу передать массив по ссылке на подпрограмму и распечатать каждый город для вывода. Однако у меня есть следующие проблемы:

1) Я могу получить доступ к каждому элементу перед циклом while в подпрограмме. Но я не могу получить доступ к элементам в цикле while. Я получаю сообщение об ошибке:

...
Use of uninitialized value in print at <filename> line 44, <GEN2> line 997 (#1)
Use of uninitialized value in print at <filename> line 44, <GEN2> line 998 (#1)
...

Ниже приведен код. Я прокомментировал, что печатает, а что нет (я пытался вырезать код, который не нужен для моего объяснения...):

@cities;

#Assume cities is loaded successfully
&loadCities(getFileHandle('cities.txt'), $NUM_CITIES, \@cities);
&printElements(getFileHandle('names.txt'), \@cities);

sub printElements{

    my $counter = 0;
    my $arraySize = scalar $_[1];

    # Prints fine!!!
    print @{$_[1][($counter)%$arraySize];

    while ((my $line = $_[0]->getline()) && $counter < 1000){

        #Doesn't print. Generates the above error
        print @{$_[1][($counter)%$arraySize];

        $counter += 1;
    }
}

2) Синтаксис Perl очень запутал меня. Я не понимаю, что происходит с @{$_[1]}[0]. Пытаюсь решить это.

  1. $ _ [1], обрабатывать значение в этом месте как скалярное значение (адрес памяти массива)
  2. @ {...}, интерпретировать то, что хранится по этому адресу памяти, как массив
  3. @{...} [x], получить доступ к элементу по индексу x

Я на правильном пути?

5 ответов

Решение

Мой первый совет, что вы должны поставить use strict; а также use warnings; в верхней части вашего сценария. Это обычно показывает довольно много вещей.

Эта строка: print @{$_[1][($counter)%$arraySize]; не имеет закрытия }, Вам также не нужны круглые скобки $counter,

Как вы упомянули, лучший / самый понятный способ получить длину массива my $arraySize = scalar @{$_[1]};,


Вы можете ознакомиться с документацией здесь для работы со ссылками. Я дам вам краткий обзор.

Вы можете объявить массив как обычно

my @array = (1, 2, 3);

Затем вы можете ссылаться на него, используя обратную косую черту.

my $array_ref = \@array;

Если вы хотите использовать ссылку, используйте @{...}, Это похоже на использование обычного массива.

print @{$array_ref};

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

my $array_ref = [1, 2, 3];
print @{$array_ref}; # prints 123

В Perl 2-мерный массив - это массив ссылок на массивы. Вот пример:

my @array = ( ['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i'] );
print @{$array[1]}; # prints def

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

my @array = ( ['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i'] );

example(\@array); # pass in an array reference

sub example {
    my @arr = @{$_[0]}; # use the array reference to assign a new array
    print @{$arr[1]};

    print @{$_[0][1]}; # using the array reference works too!
}

Теперь давайте соберем все вместе и распечатаем весь двумерный массив.

my @array = ( ['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i'] );
example(\@array);
sub example {
    my @arr = @{$_[0]};
    for my $ref (@arr) {
        print @{$ref};
    }
} # prints abcdefghi

Вы можете легко адаптировать этот пример, если хотите использовать его для своего printElements подпрограмма.


Еще одно замечание о печати элементов в массиве. Давайте возьмем эту строку из последнего примера:

print @{$ref};

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

print @{$ref} . "\n";

Что это печатает? Попытайся! Это работает?

Вот тут-то и пригодится встроенное объединение подпрограмм.

print join(" ", @{$ref}) . "\n";

Циклы for, как правило, являются лучшим способом перебора массива. Мой ответ здесь немного говорит о том, как сделать это с помощью цикла while: /questions/22673339/perl-pozhalujsta-obyasnite-mne-sleduyuschee-povedenie-while/22673349#22673349 Вы также можете проверить этот вопрос: Лучший способ перебрать массив Perl

Чтобы ссылки были немного легче понять, я предпочитаю -> синтаксис вместо синтаксиса munge-it-all-Вместе.

Вместо:

@{$_[1]}[0].

Пытаться

$_[1]->[0];

Это означает то же самое. Это легче и чище видеть. Ты это видишь $_[1] является ссылкой на массив, и что вы ссылаетесь на первый элемент в этой ссылке на массив.

Тем не менее, еще лучший способ - просто установить переменные для различных элементов в @_, Вам нужно набрать еще несколько букв, но ваш код намного проще для понимания и намного проще для отладки.

sub print_elements {
    my $file_handle      = shift;   # This isn't a "reference", but an actual file handle
    my $cities_array_ref = shift;   # This is a reference to your array

    my @cities = @{ $cities_array_ref };  # Dereferencing makes it easier to do your program

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

Итак, пройдемся по вашей подпрограмме:

sub printElements {
    my file_handle        = shift;
     my $cities_array_ref  = shift;

    my @cities = @{ $cities_array_ref };
    my $counter;
    my $array_size = @cities;     # No need for scalar. This is automatic
    while  ( my $line = $file_handle->getline and $counter < 1000 ) {
        chomp $line;
        my $city_number = $counter % $array_size;
        print $cities[$city_number]. "\n";
        $counter += 1;
    }
}

Обратите внимание, насколько проще увидеть, что происходит, просто назначив несколько переменных, а не пытаясь объединить все вместе. Я могу легко увидеть, каковы ваши параметры для вашей подпрограммы. Если вы вызвали подпрограмму с неправильным порядком параметров, вы можете легко определить ее. Также обратите внимание, что я вспыхнул $counter % $array_size и присвоил это переменной тоже. Внезапно становится очевидно, что я пытаюсь из этого выбраться.

Тем не менее, я не вижу, где вы используете $line вы получаете с getline, Я что-то пропустил?

Кстати, я мог бы сделать это без ссылки на массив в while цикл тоже:

sub printElements {
    my file_handle        = shift;
    my $cities            = shift;   # This is an array reference!

    my $counter;
    my $array_size = @{ $cities };   # I need to deref to get an array
    while  ( my $line = $file_handle->getline and $counter < 1000 ) {
        chomp $line;
        my $city_number = $counter % $array_size;
        print $cities->[$city_number]. "\n";   # That's it!
        $counter += 1;
    }
}

Посмотрите, как это -> Синтаксис позволяет легко увидеть, что $cities ссылка, указывающая на массив? Намного чище и легче понять, чем ${$cities}[$city_number],

Этот код на самом деле не скомпилируется.

print @{$_[1][($counter)%$arraySize];

наверное хочет быть:

print $_[1]->[($counter)%$arraySize];

после того, как вы исправите arraySize.

Если в результате получается указатель на массив,

print "@{$_[1]->[($counter)%$arraySize]}";

Я понял, как решить мою проблему #1 (все еще ищу помощь на моем #2, если кто-нибудь может).

Я изменился

my $arraySize = scalar $_[1];

в

my $arraySize = @{$_[1]};

И мое второе заявление о печати печатается так, как я хочу.

Кажется, что скаляр $_[1] брал адрес памяти массива, и я пытался изменить это, чтобы мой $counter вышел за пределы количества элементов в массиве.

Рекомендации меня тоже смущают! Мне всегда нравится разыменовывать их как можно скорее. Это работает для меня:

sub printElements{

    my $counter = 0;
    my $fh = $_[0];
    my @array = @{$_[1]};
    my $arraySize = scalar @array;

    # Prints fine!!!
    print @array[($counter)%$arraySize];

    while ((my $line = $fh->getline()) && $counter < 1000){

        #Doesn't print. Generates the above error
        print @array[($counter)%$arraySize];

        $counter += 1;
    }
}

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

Другие вопросы по тегам