Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
iakovlev.org

Массивы

 Массивы - это один из типов данных в перл, а вообще массивы есть практически 
 в любом языке программирования. Если скаляр - это переменная, у которой есть
 какое-то конкретное значение, то массив - это список каких-либо элементов. 
 Например (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