Кажется, я не могу правильно разыменовать массив

#arrays #perl #dereference

#массивы #perl #Разыменование

Вопрос:

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

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

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

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

 my @s1 = split(/:/, 'X:Y:Z');
my @s2 = split(/:/, 'A:B:C');
my @s3 = split(/:/, 'Q:L:P:0');
my @s4 = split(/:/, 'U:E:G');

my @array = ();
push(@array, @s1);
push(@array, @s2);
push(@array, @s3);
push(@array, @s4);

print "@arrayn";

my @sorted = sort { $a->[0] cmp $b->[0] } @array;

print "n";
foreach $thingy (@sorted)
{
    print @thingy . "n"; #result: number 0
    print $thingy . "n"; #result: reference
    #print ${$thingy} . "n"; #result: 'Not a scalar reference' error
    print ${@thingy} . "n"; #result: file name (???)
    print @{$thingy} . "n"; #result: length of the array referenced
}
  

Комментарии:

1. Не имеет прямого отношения к вашему вопросу, но вы можете рассмотреть что-то вроде этого: my @array = map [split /:/], qw(X:Y:Z A:B:C Q:L:P:0 U:E:G) .

Ответ №1:

Прежде всего, вы всегда должны помещать use strict; в начало вашей программы. Это приведет к раннему обнаружению многочисленных ошибок.

Последняя строка в вашем foreach цикле — это та, которая разыменовывает $thingy правильно. Но поскольку вы поставили @{$thingy} в левой части . оператора (конкатенации строк), массив находится в скалярном контексте, а массивы в скалярном контексте вычисляются в соответствии с их размером. Просто скажите:

 print "@{$thingy}n";
  

чтобы получить элементы @$thingy , разделенные пробелами, или вообще

 print join('|', @{$thingy}), "n";
  

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

 print @{$thingy}, "n";
  

печатать элементы вообще без разделителя.

Комментарии:

1. Это было именно то, что мне было нужно. Иногда мне трудно решить, интересен Perl или нет. В нем есть несколько самых безумных нюансов, которые я когда-либо видел. Я бы никогда не догадался, что это была моя проблема. Спасибо!

Ответ №2:

@thingy является необъявленным и неопределенным (и ненужным).

Используйте два вложенных цикла, чтобы

  1. выполните итерацию по вашему массиву с помощью ссылок на массив
  2. и затем в каждом цикле выполняйте итерацию по элементам в этом ссылочном массиве.

Вот так

 foreach my $array_ref (@sorted)
{
    foreach my $item (@{$array_ref}) {
        print $item, ",";
    }
    print "n";
}
  

Выражение @{$array_ref} разыменует вашу ссылку на массив. Тогда он используется как массив.

Добавление:

Вы могли бы заменить

 my @s1 = split(/:/, 'X:Y:Z');
my @s2 = split(/:/, 'A:B:C');
my @s3 = split(/:/, 'Q:L:P:0');
my @s4 = split(/:/, 'U:E:G');

my @array = ();
push(@array, @s1);
push(@array, @s2);
push(@array, @s3);
push(@array, @s4);
  

с

 my @array = ();
push(@array, map { [split(/:/, $_)] } qw(X:Y:Z A:B:C Q:L:P:0 U:E:G));
  

Если для сортировки требуются два критерия (первичный по первому индексу и вторичный по второму индексу), это может быть записано следующим образом:

 my @sorted = sort { $a->[0] cmp $b->[0] 
                             ||
                    $a->[1] cmp $b->[1] 
                  } @array;
  

Комментарии:

1. Как я уже говорил в своем первоначальном посте, это был всего лишь тестовый скрипт, с которым я поиграл, прежде чем попытаться вставить этот код в свой основной скрипт. Таким образом, все массивы и нажатия будут обрабатываться лучше в реальном скрипте. Я просто добавлял что-то грязное, чтобы имитировать мою ситуацию, и я включил код во фрагмент здесь для наглядности. Но я также хотел сказать спасибо за код первичной / вторичной сортировки. Я еще нигде этого не видел в своих поисках, пытаясь просто заставить сортировку работать. Это должно было стать моей следующей проблемой, но вы упростили мне задачу. Я протестировал это, и это работает!

Ответ №3:

Первое, что вам нужно сделать, это добавить это в ваш скрипт:

 use strict;
use warnings;
  

Затем вы получите предупреждение:

 Global symbol "@thingy" requires explicit package name
  

Что означает, что @thingy не определено. В perl $thingy и @thingy считаются отдельными переменными.

Другой способ создать свой массив — использовать анонимные массивы, подобные этому:

 push @array, [ split(/:/, 'X:Y:Z') ];
push @array, [ split(/:/, 'A:B:C') ];
...
  

Тогда вам не придется создавать одноразовые переменные. Или с файлом, подобным тому, который вы описываете ( t is tab):

 while (<>) {
    push @array, [ split /t/, $_ ];
}
  

Способ сортировки по нескольким столбцам из perlmonks:

 my @a = ([1,2], [3,4]);
my @b = sort {

    $a->[0] <=> $b->[0] || # the result is -1,0,1 ...
    $a->[1] <=> $b->[1]    # so [1] when [0] is same

} @a;
  

http://www.perlmonks.org/index.pl?node_id=674374

Конечно, это предполагает числовые значения в ваших полях. В противном случае используйте cmp .

Для печати:

 for my $ref (@array) {
    my $i = 0;
    for my $value (@$ref) {
        print $value; 
        print "," if ($i   < $#$ref); # comma delimited
    }
    print "n"; # end of record
}