#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
является необъявленным и неопределенным (и ненужным).
Используйте два вложенных цикла, чтобы
- выполните итерацию по вашему массиву с помощью ссылок на массив
- и затем в каждом цикле выполняйте итерацию по элементам в этом ссылочном массиве.
Вот так
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
}