Внимание! Прочитайте, пожалуйста, текст в правой колонке (внизу).
Внимание! Прочитайте, пожалуйста, текст в правой колонке (внизу). Внимание! Прочитайте, пожалуйста, текст в правой колонке (внизу). Homepage Карта сайта Версия для печати

Джентльменский набор Web-разработчика   Ларри Уолл о Perl6   Наблы Система Orphus
 

19. Хэши и массивы в PHP,  или просто о простом

[1 апреля 2002 г.]

В этой и следующих наблах я попытаюсь кратко систематизировать всю идеологию работы с переменными сложной структуры в PHP и Perl. Начнем мы с более простых вещей — то есть, с PHP, — а закончим ссылками и многомерными структурами данных в Perl. Конечно, кому-то этот материал покажется слишком простым (только не надо «гнуть пальцы»), но, надеюсь, он будет полезен всем остальным.

Но сначала давайте договоримся о терминологии. Итак, массив — переменная, хранящая сразу несколько значений. Добраться до конкретного значения можно при помощи указания его индекса — обязательно целого числа (возможно, полученного в результате вычислений). Как правило, самый первый элемент массива имеет индекс 0 (а не 1), а значит, последний элемент имеет индекс, на 1 меньший размера массива. Хэш (переводится как «мешанина») — практически то же самое, что и массив, но в качестве индекса используется не число, а произвольная строка. Индексы хэша называются ключами, а соответствующие им величины элементов — значениями. Точно так же, как в массиве не может быть двух элементов с одинаковым индексом, в хэше невозможно существование двух значений с одинаковым ключом. Скаляром я буду здесь и далее называть переменную, не являющуюся ни хэшем, ни массивом (например, строку или число).

Лирическое отступление 
Эта терминология (на мой взгляд, весьма удачная) заимствована из Perl.

Как известно, в PHP имена всех переменных необходимо предварять символом $. Вы, наверное, догадываетесь, что это в числе прочего позволяет интерпретатору подставлять значения таких переменных непосредственно в строки:

$a=10;
print "Значение $а, вставленное в строку";

В отличие от Perl, правило со знаком доллара применяется здесь абсолютно ко всем переменным, будь они массивами, хэшами или же простыми скалярами. Начнем с массивов. Обращения к элементу массива производится очень просто — например, вот так:

# пусть $A - это массив
print $A[10]; # печатает элемент с индексом 10

Конечно, допустимы и следующие конструкции:

$i=2;
print $A[$i];    # печатает 2-й элемент
print $A[$i*$i]; # печатает 4-й элемент

Чтобы вставить значение элемента массива в строку, можно написать (здесь и далее речь идет о PHP версии 4):

$i=2;
print "Проверка: $A[0]";
print "Еще раз: $A[$i]";

Лирическое отступление 
Замену переменной на ее значение в строке часто называют интерполяцией — по-моему, весьма неудачно. Тем не менее, придется придерживаться этой терминологии, чтобы не отходить от устоявшихся канонов.

Однако в более сложных случаях (когда, например, индекс равен $i*$i) придется использовать другой синтаксис:

$i=2;
print "Сложный индекс: {$A[$i*$i]}";

Лирическое отступление 
Обратите внимание, что знак $ должен идти непосредственно после открывающей фигурной скобки; именно по сочетанию {$ PHP понимает, что же имеет в виду программист.

Этот синтаксис универсален: в квадратных скобках массива может стоять все, что угодно, например:

print "Последний элемент: {$A[count($A)-1]}";

В качестве элемента массив может содержать не только скаляр, но и другой массив. В этом случае его называют «многомерный массив», или матрица, но рассматривать его лучше не как прямоугольную таблицу, а именно как «массив массивов» (потому что подмассивы вполне могут иметь разный размер, так что вполне возможно существование «треугольного массива»). Обращение к элементам двумерных массивов производится так же:

# печатает 2-й элемент 1-го подмассива
print $A[1][2];
# вставляет значение в строку
print "Значение в строке: {$A[1][2]}";

Обратите внимание, что нельзя написать:

# неправильно!
print "Значение в строке: $A[1][2]";

Это просто не сработает. Так что особо не удивляйтесь, когда вместо ожидаемого результата программа напечатает что-то вроде Array[2], не выдав даже предупреждения.

Теперь о хэшах в PHP. Вообще говоря, обращения к ним принципиально ничем не отличается от обращений к массиву — точно так же подставляются индексы (теперь они называются уже ключами), и даже точно так же осуществляется вставка значения в строку. Вот примеры:

$k="ключ";
print $H["какой-то ключ"];
print $H[$k];
print $H[$k."хвостик"];
print $H[$k]["двумерный хэш"];
print "Вставляем в строку: {$H[$k.'!']['что-то']}";
print "Особенно интересно: {$H[$k]["что-то"]}";

Можно подумать, что в последний пример вкралась опечатка, и кавычки внутри строки необходимо экранировать при помощи \ (или же просто использовать апострофы), однако это не так. Хотите верьте, хотите — нет, но PHP, видя {$...} в строке, понимает, что внутри скобок идет обращение к сложной переменной и интерпретирует его специальным образом — в частности, плюет на вложенные кавычки. Конечно, использование апострофов более удобно, потому что оно не дает повода для сбоя механизма подсветки синтаксиса в текстовом редакторе.

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

# создаем массив c индексами 0, 1, 2
$A = array('a','b','c');
# создаем двумерный треугольный массив
$A = array(
    array('a','b','c'),
    array('d','e'),
    array('f')
);
# создаем хэш
$H = array('k1'=>'a', 'k2'=>'b', 'k3'=>'c');
# более удобно - меньше апострофов
$H = @array(k1=>'a', k2=>'b', k3=>'c');
# хэш массивов
$H = @array(
    k1 => array('a','b','c'),
    k2 => array('d','e'),
    k3 => array('f'),
    k4 => $A  # подставляем целый массив
);

Массив создается простым перечислением его элементов. При создании хэша необходимо также указывать и ключи (слева от стрелочки =>). Впрочем, если ключ не указать, то вместо него будет использовано некоторое число — как будто это элемент массива, а не хэша, а число — его индекс.

Чайник 

Обратите внимание на то, насколько использование @ (оператора локального отключения предупреждений) сокращает письмо в последних примерах. Это как раз то, что нужно.

Мы знаем, как можно получить доступ к произвольному элементу массива и хэша. Однако чаще всего приходится решать и более общую задачу: выбрать и обработать сразу все элементы. Для массивов это можно сделать вот так — «в лоб»:

for($i=0; $i<count($A); $i++)
  print "Значение элемента $i: $A[$i]<br>";

Однако этот подход не годится и для хэшей: мы не можем просто сделать $i++ для перехода от одного ключа к следующему, необходимо что-то еще. Специально для перебора хэшей в PHP4 введен специальный оператор цикла:

foreach($H as $k=>$v)
  print "Ключ: $k, значение: $v<br>";

Впрочем, с его помощью можно перебирать и массивы (подставив вместо хэша $H массив $A). В этом случае, конечно же, в переменной $k будет сохраняться индекс элемента, а в $v — его значение.

Чайник 

Разумеется, имена $k и $v были выбраны совершенно произвольно.

Если же при переборе вас совершенно не интересуют индексы элементов, то цикл можно упростить:

foreach($A as $v)
  print "Значение: $v<br>";

Лирическое отступление 
Нужно заметить, что цикл foreach теоретически работает несколько медленнее, чем «лобовой» перебор элементов массива. Однако для небольших массивов замедление несущественно.

При всяком переборе возникает вопрос: в каком порядке будут идти элементы масива или хэша?.. Для обычных массивов ответ более-менее очевиден — в порядке следования индексов. Но что же произойдет с хэшами?.. И тут проявляется еще одна замечательная особенность PHP: перебор хэша всегда осуществляется в том порядке, в котором создавались его элементы. То есть, на самом деле мы получаем возможность доступа не только по ключу, но и по порядковому номеру (правда, лишь косвенно — если в хэше нет ключа со значением 10, написать напрямую $H[10] нельзя). Вот почему сами разработчики PHP предпочитают называть хэши ассоциативными массивами — им не нравится слово «мешанина», и совершенно справедливо.

В действительности аналогия между массивами и хэшами значительно глубже, и без преувеличения можно заявить, что массив в PHP — этот тот же самый хэш, однако ключи у него целочисленные, начинаются с 0 и идут без перерыва. Так что вполне возможно создать в программе хэш, который будет выглядеть как обычный массив, однако при переборе его значения будут идти не в порядке увеличения индексов, а, например, наоборот:

$AH = array(
  2 => 'c',
  1 => 'b',
  0 => 'a',
);
foreach($AH as $v) print $v;

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

Лирическое отступление 
Обратите еще внимание на, казалось бы, лишнюю запятую перед закрывающей круглой скобкой. На первый взгляд (особенно после Java) кажется, что тут должна быть синтаксическая ошибка, однако ее нет ни в PHP, ни в Perl, ни даже в Си. Почему?.. Все очень просто: такой синтаксис позволяет в будущем легко модифицировать программу и добавить новый элемент в конец массива. Если бы не было финальной запятой, вы вполне могли бы забыть ее напечатать, вставив новую строку.

Вообще, хэши — довольно «медленные» объекты с точки зрения затрат процессорного времени. Вы, возможно, спросите: раз массивы — это в действительности хэши, они будут «тормозить»?.. Оказывается, нет. И это все благодаря тому же свойству упорядоченности ассоциативных массивов: раз все уже упорядочено, найти нужный элемент довольно просто.

Лирическое отступление 
В действительности, конечно, для массивов применяются дополнительные оптимизации, но они «не видны» программисту на PHP.

В следующей набле мы рассмотрим хэши и массивы в языке Perl. Как вы увидите, в этом языке сложные объекты имеют несколько иные свойства (более выраженные).

 
Рекламный блок
   

Важное объявление:
    автор категорически против копирования и распространения в Интернете всех статей «Куроводства» с возрастом, меньшим 6 месяцев. Печальный опыт «расползания» чрезвычайно устаревших ошибочных версий статьи про Apache действительно объясняет такое решение.

Орфография на «Куроводстве»:
    если вы заметили орфографическую, стилистическую или другую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter. Выделенный текст будет немедленно отослан вебмастеру, а Вы даже ничего и не заметите — настолько быстро все произойдет.

На заметку:
    если вы уже вскипели насчет дизайна этой страницы, то присмотритесь повнимательнее к названию, почитайте FAQ, сходите по лебедевским местам, как это уже предлагалось выше. Можно ли считать пародию плагиатом? Надеюсь, что нет.

Параметры этой страницы
   
GZip

Ссылки от спонсоров
   


Дмитрий Котеров | 1 апреля 2002 г. ©1999-2016 | Генеральный спонсор: Хостинг «Джино» | Контакт Вернуться к оглавлению