Массивы
Массивы - это один из типов данных в перл, а вообще массивы есть практически
в любом языке программирования. Если скаляр - это переменная, у которой есть
какое-то конкретное значение, то массив - это список каких-либо элементов.
Например (5, "Hacked", $site) это список который содержит цифру, строку и переменную.
Чтобы создать массив, надо просто присвоить ему какое-либо значение, например:
@deface=qw ( Hacked by Me );
Эта строчка создаст массив с 3 элементами. Оператор qw позволяет записывать
элементы массива без кавычек.
Чтобы вывести содержимое массива на экран, надо использовать тот же оператор,
что и для обычных переменных:
print "@array";
Если вам надо напечатать только какие-то отдельные элементы, то нужно
воспользоваться индексами, например, создадим массив @lang:
@lang=qw ( Perl PHP C++ Java Asm );
У каждого из элементов массива есть свой порядковый номер, причём отсчёт
начинается с 0:
0 1 2 3 4
Perl PHP C++ Java Asm
Итак, если надо распечатать, например только 1 и 3 элементы массива, то:
print $lang[1];
print $lang[3];
Один из способов преобразования скаляра в массив - оператор split.
Рассмотрим эту строку:
@words=split(//, "Hacked by me");
Этот оператор берёт строку Hacked by me, делит её по словам
(для этого служит шаблон //, о шаблонах - позже) и помещает слова в массив,
причём каждое слово содержится в отдельном элементе массива.
Раасмотрим пример : пусть имеется массив , в котором каждый элемент имеет имя .
Задача - как по имени элемента получить его значение ?
%form_test = ('a1' => 11 ,'a2' => 22 ,'a3' => 33 );
$name="test";
$tmp = eval "\\\%form_$name";
$key_val = $$tmp{"a2"};
В следующем примере организуем массив , элементами которого являются
другие массивы , причем нефиксированного размера :
@a=(["a","b","c"],["d","e"],["f"]);
foreach $b (@a){
foreach $c (@$b){
print "$c\n";
}
print "====\n";
}
$z=$a[1][1];
print "--$z\n";
Чтобы обьединить 2 массива в один массив :
@array1 = (1, 2, 3);
@array2 = (4, 5, 6);
@bigarray = (@array1, @array2);
Чтобы выделить часть массива :
@array = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
@array2 = @array[2..3];
В следующем примере дан плоский массив.
Задача - увеличить значение каждого элемента на 1 :
my @arr = (1, 2, 3, "a");
print "@arr\n";
Без использования индекса :
foreach (@arr){
++$_;
}
print "@arr\n";
foreach my $current (@arr){
++$current;
}
print "@arr\n";
С использованием индекса :
foreach (0..@arr-1){
++$arr[$_];
}
print "@arr\n";
foreach my $index (0..@arr-1){
++$arr[$index];
}
print "@arr\n";
foreach (0..$#arr){
++$arr[$_];
}
print "@arr\n";
my $index=0;
foreach (@arr){
++$arr[$index]; # или ++$_;
++$index;
}
print "@arr\n";
В следующем примере имеется неотсортированный массив с повторяющимися значениями .
@taxon = (1,3,5,5,5,3,1,5,5,5,5,3,3,7,9,10);
Задача - оставить только уникальные значения .
Первый вариант - простой перебор :
my $i=0;
while(defined $taxon[$i])
{
my $n = $i + 1;
while (defined $taxon[$n])
{
if ($taxon[$i] == $taxon[$n]) { splice(@taxon, $n, 1); }
else { $n++; }
}
$i++;
}
А вот более изящное решение :
%z = map { $_=>0 } @taxon;
@taxon = keys %z;
И еще один вариант :
@unique = grep { ++$count{$_} < 2 } qw(a b a c d d e f g f h h);
Сортировка числового массива по возрастанию :
@array = (8, 2, 32, 1, 4, 16);
print join(' ', sort { $a <=> $b } @array), "\n";
ASCII-сортировка
@languages = qw(fortran lisp c c++ Perl python java);
print join(' ', sort @languages), "\n";
Сортировка по алфавиту :
use locale;
@array = qw(ASCII ascap at_large atlarge A ARP arp);
@sorted = sort { ($da = lc $a) =~ s/[\W_]+//g;
($db = lc $b) =~ s/[\W_]+//g;
$da cmp $db;
} @array;
print "@sorted\n";
Реверс-сортировка :
sort { $b <=> $a } @array;
Удаление из списка :
@list = splice @array, $from, $length, список
В следующем примере нужно распечатать элементы , которые встречаются в
массиве 2 раза :
@crops = qw(wheat corn barley rice corn soybean hay
alfalfa rice hay beets corn hay);
@duplicates = grep { $count{$_} == 2 }
grep { ++$count{$_} > 1 } @crops;
print "@duplicates\n";
Имеются 2 параллельных массива - из номеров страниц и примечаний к этим страницам.
Отпечатать оба массива , отсортировав их по номерам :
@page = qw(24 75 41 9);
@note = qw(p.24-text p.75-text p.41-text p.9-text);
@note_sorted = @note[sort { $page[$a] <=> $page[$b] } 0..$#page];
Сортировка массива по убыванию :
print "по убыванию: ",
join(" ", sort { $b <=> $a } 3,4,1,5,9,7),"\n";
Переворачивание массива :
@stooges = (3,4,2,5,6,7,8);
@segoots = reverse(@stooges);
Разбиение строки на набор слов по пробелу :
$str = "I'm not as think as you stoned I am";
@words = split (/ /, $str);
Добавление элемента в массив :
@array = ("Quarter","Dime","Nickel");
push(@array, "Penny");
Вставка-сдвиг на начальную позицию :
unshift(@array, "Dollar");
Удаление последнего элемента :
pop(@array);
Удаление первого элемента :
shift(@array);
Удаление элемента с индексом index :
delete $array[index]
В следующем примере имеется база клиентов , в которой нужно найти
одного с критериями city , music :
@client = (
{ name => "serg", city => "Москва", music => "рок" },
{ name => "alex", city => "Москва", music => "джаз" },
{ name => "john", city => "Киев", music => "рок" },
{ name => "smith", city => "Москва", music => "рок" }
);
%query = ( city => 'Москва', music => 'рок' );
@res = findRestaurants(\@client, \%query);
print "$res[0]->{name}\n";
sub findRestaurants {
my ($client, $query) = @_;
return grep {
$query->{city} ?
lc($query->{city}) eq lc($_->{city}) : 1
and $query->{music} ? $_->{music} : 1
} @$client;
}
Пример maximal obfuscation :
print map( { chr }
('10611711511603209711011111610410111' .
'4032112101114108032104097099107101114')
=~ /.../g
), "\n";
Транспонирование матрицы :
@matrix = ( [1, 2, 3], [4, 5, 6], [7, 8, 9] );
foreach $xyz (@matrix) {
print "$xyz->[0] $xyz->[1] $xyz->[2]\n";
}
@transposed = map { $x = $_;
[ map { $matrix[$_][$x] } 0 .. $#matrix ];
} 0 .. $#{$matrix[0]};
print "\n";
foreach $xyz (@transposed) {
print "$xyz->[0] $xyz->[1] $xyz->[2]\n";
}
Распечатка первых простых чисел , меньших 1000 :
foreach $num (1 .. 1000) {
@expr = map { '$_ % ' . $_ . ' &&' } 2 .. int sqrt $num;
if (eval "grep { @expr 1 } $num") { print "$num " }
}
То же самое :
CANDIDATE: foreach $num (1 .. 1000) {
foreach $factor (2 .. int sqrt $num) {
unless ($num % $factor) { next CANDIDATE }
}
print "$num ";
}
Следующий пример показывает , как в процедуру передать 2 ссылки на 2 массива
и найти сумму членов этих массивов с одинаковыми индексами :
@array1 = (1, 3, 5);
@array2 = (2, 4, 6);
$ref1 = \@array1;
$ref2 = \@array2;
@sum = &array_adder($ref1, $ref2);
print "\@sum = (", join(',',@sum), ")\n";
sub array_adder{
my($ref1, $ref2) = @_;
my $i = 0;
my @sum;
for($i = 0; $i <= $#{$ref1} ; $i++){
$sum[$i] = ${$ref1}[$i] + ${$ref2}[$i];
}
return @sum;
}
Пример : имеется матрица 3х3.
Нужно отсортировать ее по 2-му столбцу .
Вариант решения :
@a = ( [4, 5, 6],
[7, 8, 9],
[1, 2, 3]
);
for ($i=0;$i<2;$i++){
for ($j=1;$j<3;$j++) {
if ($a[$i][1]>$a[$j][1]) { # делаем swap
$swap0=$a[$i][0];
$swap1=$a[$i][1];
$swap2=$a[$i][2];
$a[$i][0]=$a[$j][0];
$a[$i][1]=$a[$j][1];
$a[$i][2]=$a[$j][2];
$a[$j][0]=$swap0;
$a[$j][1]=$swap1;
$a[$j][2]=$swap2;
} }}
Ссылки
До perl5 в перле было всего три (основных) типа данных и имя переменной
полностью определяло, данные какого типа она хранит
$x - хранит ЗHАЧЕHИЕ (число или строку)
@x - хранит список ЗHАЧЕHИЙ
%x - хранит список пар строка->ЗHАЧЕHИЕ
В perl5 всё осталось также, только понятие ЗHАЧЕHИЯ было расширено - теперь это
может быть ССЫЛКА на массив или хеш. очень важная тонкость - в отличие от
самого массива или хеша, ССЫЛКА на них является скаляром:
@a = (1,2);
$a = [ (1,2) ]; # квадратные скобки превращают массив/хеш в ССЫЛКУ на массив
%b = (1=>2);
$b = { (1=>2) }; # фигурные скобки превращают массив/хеш в ССЫЛКУ на хеш
Обратите внимания - круглые скобки используются только для группировки, а
квадратные и фигурные являются ОПЕРАТОРОМ, превращающим СОСТАВHОЕ ЗHАЧЕHИЕ в
ССЫЛКУ. Внутри []/{} круглые скобки обычно опускают, я привёл их лишь для
удобства понимания единственной существенной разницы между этими действиями
Теперь несложно сконструировать массив массивов:
@c = ( [1,2], [2], [3,4,5] );
На самом деле, конечно, это массив ССЫЛОК на массивы. но к его элементам можно
обращаться, не задумываясь об этом - $c[0][1]
Или создадим хеш хешей:
%d = ( C=> {Speed=>Good, Readability=>Bad}, Perl=>{Speed=>hmmm} );
Опять же, $d{Perl}{Speed} будет работать
Если же вы и внешние круглые скобки замените на []/{}, то опять получите
скаляр, и его надо будет присваивать тоже скаляру:
$e = [ [1,2], [3,4] ]; # ССЫЛКА на массив, состоящий из ССЫЛОК на массивы
Поскольку перловые функции обычно ожидают "развёрнутый" массив/хеш,
пользоваться таким скаляром будет неудобно. да и к элементам его доступ
неочевиден - $e->[0][1]. собственно, "->" - оператор разыменования ССЫЛОК, и
для доступа к элементам @c он тоже используется, но перл вставляет его неявно:
$c[0][1] эквивалентно $c[0]->[1]. То есть, $c[0] возвращает нам ССЫЛКУ на
массив, стрелка её разыменовывает (превращает ССЫЛКУ в массив) и затем второе
индексирование "[1]" возвращает нам элемент этого массива.
$e->[0][1] на самом деле работает как $e->[0]->[1]:
разыменовываем, берём элемент, снова разыменовываем, и снова берём элемент. с
массивами хешей, и со сколь угодно более сложными структурами данных всё точно
также. разумеется, их можно произвольно смешивать:
$a = [ 1, [2,3], {a=>b}, [[4,5]] ];
1 == $a->[0]
2 == $a->[1][0]
b == $a->[2]->{a} == $a->[2]{a}
5 == $a->[3][0][1]
Операторы []/{} создают КОПИИ переданных им ЗHАЧЕHИЙ. если вы хотите вместо
этого получить ССЫЛКУ на ТЕ ЖЕ данные, используйте оператор "\". кстати, эти
операторы можно комбинировать:
@a = (1,2);
@b = (3,4);
@c = ( [@a], [@b] ); # @c содержит (ССЫЛКИ на) копии массивов a и b
@d = ( \@a, \@b ); # @d содержит ССЫЛКИ на массивы a и b
@e = ( [\@a, \@b], [[@a], [@b]] );
@e - трёхмерный массив,
$e[0][0][$i] эквивалентно $a[$i]
$e[0][1][$i] эквивалентно $b[$i]
$e[1][0][$i] содержат копии $a[$i]
$e[1][1][$i] содержат копии $b[$i]
Т.е. теперь присваивания $a[$i] изменят также ЗHАЧЕHИЯ $d[0][$i] и $e[0][0][$i]
и наоборот. ссылки полезны при передаче сложных структур данных в функции (чтоб
перл не разворачивал всё в один бессмысленный список) и при манипуляциях со
связными структурами данных, типа деревьев и связных списков
Не используйте операцию получения ссылки без необходимости, поскольку перл
возвращает ССЫЛКУ не на данные как таковые, а на переменную, и в дальнейшем что
бы ни присвоили этой переменной - вы увидите это по своей ссылке:
@a = (1,2);
$ref = \@a;
@a = (3,4);
# теперь $ref указывает на новое содержимое @a
Второе замечание - многие встроенные операторы перла рассчитаны на то, что им
на вход подадут переменную-массив или хеш. Но никак не ССЫЛКУ. для того, чтобы
превратить одно в другое, используются конструкции @{...} и %{...}:
push @{$c[0]}, 0; # $c[0] - ССЫЛКА на массив, @{$c[0]} - сам массив
%d = ( C=> {Speed=>Good, Readability=>Bad}, Perl=>{Speed=>hmmm} );
print keys %{$d{C}} # $d{C} - ССЫЛКА на хеш, %{$d{C}} - сам хеш
и когда нужно вывести сами новости в количестве $n штук на страничку.
Ссылка - это скаляр , который указывает на массив или хэш или что-то еще .
Одна из форм обращения со ссылками - обратный слэш .
Если слева от переменной мы ставим \ , мы получаем ссылку на эту переменую .
$aref = \@array; # ссылка на @array
$href = \%hash; # ссылка на %hash
Далее ссылку можно копировать как скаляр :
$xy = $aref; # ссылка на @array
$p[3] = $href; # ссылка на %hash
$z = $p[3]; # ссылка на %hash
Это был пример того , как сделать ссылку на переменную с именем .
А можно ли сделать ссылку на переменную , у которой нет имени ?
В перле это делается с помощью анонимных переменных .
[ ITEMS ] - форма создания анонимного массива .
{ ITEMS } - форма создания анонимного хэша .
$aref = [ 1, "foo", undef, 13 ]; # $aref - ссылка на массив
$href = { APR => 4, AUG => 8 }; # $href - ссылка на хэш
Ссылка , которую мы получили во втором варианте , ничем не хуже той ,
которую мы получили в первом случае .
$aref = [ 1, 2, 3 ];
@array = (1, 2, 3);
$aref = \@array;
Как использовать ссылки ? Для начала ссылку можно присвоить какой-нибудь
переменной , после чего :
@a @{$aref} сохраним ссылку
reverse @a reverse @{$aref} перевернем массив
$a[3] ${$aref}[3] возьмем элемент массива
$a[3] = 17; ${$aref}[3] = 17 изменим элемент массива
Видно , что с массивом можно оперировать 2-мя способами одинаково ,
дело только за вкусом .
В случае с хэшем картина аналогичная :
%h %{$href} сохраним ссылку
keys %h keys %{$href} получить ключи из хэша
$h{'red'} ${$href}{'red'} элемент хэша
$h{'red'} = 17 ${$href}{'red'} = 17 назначить элемент
${$aref}[3] - довольно неприглядный синтаксис для элемента массива , куда круче
$aref->[3]
Аналогично для хэша :
$href->{red}
Следующий пример :
@a = ( [1, 2, 3],
[4, 5, 6],
[7, 8, 9]
);
@a - массив из 3-х элементов , каждый из которых представляет из себя
ссылку на другой массив .
Тут мы подходим к оперированию в перл таким понятием , как 2-мерный массив :
$a[1]->[2] равен 6 , $a[0]->[1] равен 2.
В общей форме это как $a[ROW]->[COLUMN] .
Кстати говоря , вместо $a[1]->[2] мы можем написать $a[1][2] - это то же самое .
Следующий пример : имеются записи в базе данных "город-штат"
Chicago, Illinois
New York, New York
Albany, New York
Springfield, Illinois
Trenton, New Jersey
Evanston, Illinois
Нужно отсортировать по принципу :
Illinois: Chicago, Evanston, Springfield.
New Jersey: Trenton.
New York: Albany, New York.
Решение :
1 while (<>) {
2 chomp;
3 my ($city, $state) = split /, /;
4 push @{$table{$state}}, $city;
5 }
6
7 foreach $state (sort keys %table) {
8 print "$state: ";
9 my @cities = @{$table{$state}};
10 print join ', ', sort @cities;
11 print ".\n";
12 }
В 4-й строке формируется хэш %table, эта строка аналогичнв
push @array, $city;
В 9-й строке идет извлечение городов для каждого штата , она аналогична строке
@cities = @array;
Подведем некоторые итоги по ссылкам :
- Ссылки можно делать на что угодно , включая скаляры , функции , другие ссылки .
- Фигурные скобки по возможности нужно опускать ,например :
@$aref - это то же самое , что и @{$aref}, и
$$aref[1] - то же , что и ${$aref}[1].
- Для проверки того , является ли переменная ссылкой ,
нужно использовать фунцию ref .
Она должна вернуть true .Для хэша она возвращает HASH , для массива - ARRAY .
|
zizmo | Очень приятно. Сжато и емко. Нашел что искал. Спасибо. 2007-09-04 16:17:10 | |
|