Лекція № 10 Тема: Масиви та списки. Операції над масивами. План Поняття массиву Поняття списку Асоціативні масиви Інструкції list() Інструкція array() і створення багатовимірних масивів Видалення масивів Злиття масивів Перебір масиву Сортування масивів Сортування списку Поняття масиву Масив — це впорядкований набір даних. Кожен елемент масиву має індекс або ключ. Індекс (ключ) служить для однозначної ідентифікації елементу усередині масиву. У одному масиві не може бути двох елементів з однаковими індексами. Працювати без масиву не дуже зручно, думаю, ви і самі це розумієте. Існує ряд завдань, де без використання масиву просто не обійтися. Прикладом може послужити простій список друзів. Не будете ж ви створювати 20 змінних, що містять імена ваших друзів? Набагато рациональнее створити один масив, що містить 20 елементів. Якщо ви програмували на С або Pascal, то знаєте, що довжина масиву обмежена. При цьому, якщо ви наперед не знаєте, скільки елементів міститиме ваш масив, означає вам потрібно використовувати динамічні структури (список, стік, черга). У PHP все набагато простіше — довжину масиву не потрібно задавати при оголошенні масиву, довжина буде автоматично збільшена при додаванні нового елементу в масив. Все це стає можливим тільки завдяки тому, що PHP — інтерпретатор, а не компілятор. Дуже важливою особливістю PHP є те, що він, на відміну від інших мов, дозволяє створювати масиви будь-якої складності безпосередньо в тілі програми (сценарію). Це буває дуже корисним, коли потрібно створити масив, на основі деяких, наперед невідомих параметрів. Розглянемо простий спосіб ініціалізації масиву: $Реор1е[0]="Коля"; $People[1]="Витя"; $Реор1е[2]="Дима"; $Реор1е[3]="Марк"; PHP дізнається про те, що створюється масив по квадратних дужках. У квадратних дужках указується індекс (ключ) окремого елементу масиву. У приведеному прикладі ми оголосили масив $Реор1е, що складається з чотирьох елементів. Проте надалі нам ніхто не перешкодить ініціалізувати ещеодин (або скільки потрібно) новий елемент масиву. У PHP розмір масиву не задається явно, як в інших мовах програмування. Звернутися до елементу масиву можна по імені масиву і індексу елементу усередині масиву: echo $People[1]; Знаючи кількість елементів масиву, ми можемо вивести всі елементи масиву за допомогою циклу: for($i=0; $1<количество; $i++)echo "$People[$i]<br>"; Якщо ми не знаємо точну кількість елементів масиву, можна використовувати функцію count(). Ця стандартна функція визначає розмір масиву, тобто кількість елементів в нім. Відповідно, з використанням функції count() вищенаведений цикл виглядає таким чином: for($i=0; $i<count($People); $i++)echo "$People[$i] <br>"; Поняття списку Масиви, індексами яких є числа, що починаються з нуля без пропусків, називатимемо списками. З технічної точки зору різниці між масивами і списками немає. Не знаю як вам, але мені зручніше створювати списки без вказівки індексу — хай це зробить за мене PHP. Адже я можу ненароком вказати вже існуючий індекс. Крім того, таке автоматичне створення масивів зручне при записі текстового файлу в масив для подальшої обробки. Ось приклад автоматичного створення масиву: $Реор1е[]="Коля"; $Реор1е[]="Витя"; $People[]="Дима"; При цьому PHP почне нумерацію з найменшого незайнятого значення індексу. Зокрема, якщо змінна $People тільки зараз ініціалізувалася, то нумерація буде почата з нуля. Надалі, з кожним новим елементом масиву індекс збільшуватиметься на одиницю. Як бачите все просто і зручно — не потрібно указувати індекси. Проте, це якраз той випадок, коли ми не знаємо точного числа елементів в масиві. Відповідно, при роботі з такими масивами доцільно використовувати функцію count(), яка визначає кількість елементів в масиві. Розглянемо невеликий приклад — читання текстового файлу в масив. Забігаючи наперед, скажу, що функція fopen() використовується для відкриття файлу, функція feof() істинна, якщо досягнутий кінець файлу, а функція fgets() читає рядок з файлу. При роботі з файлами ми не знаємо, скільки рядків міститиме конкретний файл. Програмуючи на С або Pascal, нам потрібно було б створити динамічний список, що викликає проблеми у програмістів, що починають, не знайомих з принципами роботи з динамічною пам'яттю. У PHP все просто — ви можете додати скільки завгодно елементів в масив, не указуючи їх номера. Нові елементи масиву будуть додані в кінець масиву. // Відкриваємо файл $f = fopen("file.txt","rt") or die("Помилка"); // Читаний відрядковий while (!feof ($f)) { // Заносимо новий елемент (рядок, прочитаний з файлу) // у кінець масиву $File[ ] = fgets(5f,50); } // Закриваємо файл fclose($f); Асоціативні масиви Ми домовилися, що масиви, індексами яких є числа що починаються з нуля, називатимемо списками. Проте в PHP індексом масиву може бути і рядок. Причому на цей рядок не накладаються ніяких обмежень: вона може містити пропуски, спеціальні символи, також немає обмежень на довжину рядка. Масиви, індексами яких є рядки, називаються асоціативними. Індекси асоціативних масивів називаються ключами. Приклад асоціативного масиву: $Реор1е["Іванов"] = "Іван"; $Реор1е["Сидоров"] = "Микола"; $Реор1е["Петров"] = "Петро"; Доступ до елементів асоціативних масивів здійснюється так само, як і до елементів звичайних масивів, і називається доступом по ключу: echo $Реор1е["Іванов"]; Інструкції list() Припустимо, що у нас є масив, що складається з трьох елементів: $People[0] = "Іван"; $People[l] = "Микола"; $People[2] = "Петро"; Тепер допустимо, що в якийсь момент нам необхідно передати значення всіх трьох елементів масиву, відповідно, трем змінним $lvanov, $sidorov, $Petrov, у які ми хочемо записати імена Іванова, Сидорова і Петрова. Це можна зробити так: $Ivanov=$People[0]; $Sidorov=$People[l]; $Petrov=$People[2]; Погодитеся, що якщо масив великий, то такий підхід не дуже зручний. Існує раціональніше рішення — використовувати інструкцію list(), яка спеціально для цього призначена: list($Ivanov,$Sidorov,$Petrov) = $Реор1е; Якщо ж нам потрібні імена тільки Сидорова і Петрова, перший параметр можна пропустити: list(,$Sidorov,$Petrov) = $Реор1е; Інструкція array() і створення багатовимірних масивів Інструкція array() використовується спеціально для створення масивів. При цьому вона дозволяє створювати порожні масиви, чого ніяк не можна зробити розглянутими раніше механізмами. Методи використання інструкції array() розглянемо на наступних прикладах: $А = array(); // створює порожній масив $А = array("Иванов"=>"Иван","Петров"=>"Петр"); // створює acоціативний масив з двома елементами$А .= array ("Иванов","Петров"); // створює список з двома // елементами. Індекси нумеруються з нуля. Інструкцію array() зручно використовувати для створення багатовимірних масивів. Припустимо, що ми хочемо створити масив, елементами якого будуть асоціативні масиви з ключами name, sex і email. Створити такий масив можна за допомогою наступних операторів: $A["Ivanov"] = аггау("name"=>"Иванов И.", "sex"=>"M", "email" => "ivanov@mail.ru"); $A["Petrov"] = аггау("пате"=>"Петров И.", "sex"=>"M", "email" => "petrov@mail.ru"); SA["Sidorova"] = аггау("пате"=>"Сидорова. H", "sex"=>"F" "email" => "sidorova@mail.ru"); Це ж саме можна записати в наступному вигляді: $А = array ( "Ivanov" = аггау("пате"=>"Иванов И.", "sex"=>"M", "email" => "ivanov6mail.ru") "Petrov" = аггау("пате"=>"Петров И.", "sex"=>"M", "email" => "petrov@mail.ru") "Sidorova" = аггау("пате"=>"Сидорова. H", "sex"=>"F", "email" => "sidorova@mail.ru") ); Дістатися до елементів масиву можна так: echo $Af"Ivanov"]["name"]; // виведе Іванов І. echo $A["Ivanov"]["email"]; // виведе ivanov@ivanov.org Багатовимірні масиви в РНР більше подібні на записі (record) в мові Pascal або структури в мові C, чим на багатовимірні масиви в звичайному розумінні. Операції над масивами Ми вже познайомилися з основною операцією над масивами — доступ по ключу, тому розглядати її зараз я не бачу сенсу. Зараз ми розглянемо три операції з масивами: Видалення. Злиття. Перебір. Видалення масивів Для видалення масиву, як і будь-якої іншої змінної, використовується вже знайома нам функція UnSet(): UnSet($People); Звичайне видалення може потрібно для дуже великого масиву, щоб звільнити використовувану ним пам'ять. Злиття масивів Стандартне злиття Злиття (конкатенація) — це операція створення масиву, що складається з елементів декілька інших масивів. Злиття масивів — це дуже небезпечна операція, тому прагніть уникати її. Результат злиття підкоряється своїй логіці, забувши про яку можна втратити дані. Злиття реалізується за допомогою оператора +. Отже, хай у нас є два масиви: $А = аrrау("1"=>"первый","2"=>"второй"); $В=аrrау("3"=>"третий","4"=>"четвертый"); Тепер, давайте злитимемо ці два масиви в один масив $С: $С = $А + $В; Відразу потрібно сказати? що оператор «+» для масивів не комутативний, а це означає, що $А + $В не рівно $В + $А. В результаті злиття $А + $В ми отримаємо: "1"=>"первый", "2"=>"второй","3"=>"третий","4"=>"четвертый" // саме в такому порядку А в результаті $В + $А, отримаємо такий масив: "3"=>"третий", "4"=>"четвертый","1"=>"первый","2"=>"второй" Особливо уважним потрібно бути при злитті списків. Припустимо, у нас знову є два масиви: $А = array(10,ll,12); $В = array(15,17,18); В результаті злиття $С = $А + $В ми отримаємо наступний масив: 10,11,12 Несподіваний результат чи неправда. Швидше за все ви чекали отримати масив array (10, 11, 12, 15, 17, 18). Проте в PHP із цього приводу свої правила. При злитті масивів, в яких містяться елементи з однаковими індексами (ключами), в результуючому масиві залишиться елемент першого масиву, причому на тому ж самому місці. А в приведеному прикладі елементи масиву $А мали індекси 1, 2, 3 і елементи масиву $в мали індекси 1, 2, 3. А оскільки масив $А при складанні стоїть першим, то його елементи і є результуючими. Можете переконатися в цьому ще раз таким чином: $А = array(10,11,12); $В = array(15,17,18); $А = $А + $В; // або $A += $В; В даному прикладі елементи масиву $А не зміняться. Змінити елементи масиву можна тільки за допомогою прямого перебору (див. наступний розділ). Злиття за допомогою функції array_merge() Функція array_merge() призначена для злиття двох масивів. Причому при злитті масивів ця функція усуває всі недоліки оператора +. Вона сполучає масиви, передані їй як аргументи, в один великий масив. Якщо в масивах, що сполучаються, зустрічаються однакові ключі (ключ=>значение), то в результуючий масив поміщається пара ключ=>значение з масиву, який стоїть правіше в списку аргументів функції array_merge(). Проте це правило не зачіпає списки, тобто масиви з числовими індексами. В цьому випадку елементи з числовим індексом, що повторюється, поміщаються в кінець результуючого масиву у будь-якому випадку. Проілюструємо сказане прикладом: $А = array(10,ll,12) ; $В = array(15,17,18); $С = $А + $В; // $С = array(10,ll,12); $С = array_merge($A,$B); // $С = array(10,ll,12,15,17,18); Таким чином, функцію array_merge() дуже зручно використовувати для злиття списків. Перебір масиву Для перебору масивів в PHP можна використовувати два методи: непрямий перебір; прямий перебір. Непрямий перебір Принцип непрямого перебору полягає в тому, що спочатку обчислюється черговий індекс (ключ), а потім по цьому індексу побічно знаходиться значення елементу масиву. Наприклад: $Реор1е[0]="Коля"; $Реор1е[1]="Витя"; $Реор1е[2]="Дима"; for($i=0; $i<count($People); $i++) echo "$People[$i] <br>"; Це ми з вами перебрали список, індексами якого є цілі числа. Для цього ми використовували цикл for, в якому перебирали індекси, послідовно збільшуючи їх на 1, і по індексах отримували елементи масиву. Проте, що робити, якщо нам потрібно перебрати не список, а асоціативний масив? Адже в асоціативному масиві індексом (ключем) є не число, а рядок: $Реор1е["Іванов"] = "Іван"; $Реор1е["Сидоров"] = "Микола"; $Реор1е["Петров"] = "Петро"; Виконати перебір такого масиву допоможе наступний цикл: for(Reset(?People); ($k = key($People)); Next($People)) . echo "$k = $People[$k] <br>"; Тут функція Reset() встановлює поточний елемент масиву SPeopIe[] на першу позицію, як би «перемотує» масив на початок. Функція key() повертає ключ поточного елементу. Якщо немає більше елементів в масиві, то ця функція поверне 0, і цикл припинить свою роботу. Функція Next() переміщає поточний елемент на одну позицію вперед (тобто переходить до наступного елементу масиву). У тілі циклу можна також використовувати функцію current(), яка повертає значення поточного елементу. З використанням цієї функції вищенаведений цикл можна переписати так: for(Reset($People); ($k = key($People)); Next($People)) echo "$k =".current($People)."<br>"; До цих пір ми перебирали асоціативний масив з початку в кінець. Проте, в PHP є можливість перебору такого масиву і з кінця. При цьому вам лише треба використовувати функції End() і Prev(), замість функцій Reset() і Next(). Перша з них встановлює поточний елемент в кінець масиву, а друга переміщає поточний елемент на одну позицію назад (тобто переходить до попереднього елементу): for(End($People); ($k = key($People)); Prev($People)) echo "$k = {$People[$k]} <br>"; Прямий перебір Принцип прямого перебору полягає в тому, щоб при кожній ітерації циклу одночасно отримувати і ключ, і значення поточного елементу масиву. Для прямого перебору у версіях, починаючи з PHP 4, використовується цикл foreach, який ми вже неодноразово використовували в цій книзі: foreach ($People as $key => $value) echo "$key = $value"; Недоліки непрямого перебору Непрямий перебір дуже простий, а код, написаний з використанням непрямого перебору, дуже читаний. Навіть якщо ви взагалі не знаєте PHP, ви розумієте, що тут і до чого. Проте непрямий перебір має істотні недоліки. Один з недоліків повинен відразу кинутися вам очі: якщо в масиві зустрінеться елемент з індексом 0, то після того, як він буде переданий в цикл функцією key(), цикл припинить свою роботу. І ще один недолік: ви не можете використовувати непрямий перебір у вкладених циклах, оскільки функція Next() другого циклу змінить положення поточного елементу в першому циклі. Прямий перебір позбавлений цих недоліків. Сортування масивів Для сортування масивів в PHP призначено багато різних функцій, але ми зараз розглянемо тільки основні з них: asort() і arsort() — сортування по значеннях; ksort() — сортування по ключах; sort() — сортування списку; array_reverce() — «перевертання» масиву; shufile() — перемішування списку. Сортування масиву по значенню. Функції asort() і arsort() Функція asort() виконує сортування масиву по значеннях елементів. При цьому вона розташовує елементи масиву таким чином, що їх значення йшли в алфавітному порядку (якщо це рядки) або в зростаючому порядку (якщо це числа). Функція arsort() виконує сортування по значеннях в зворотному порядку. Припустимо, що у нас є такий масив: $FIO=array("Sidorov"=>"Nikolay","Ivanov=>"Alexandr","Petrov"=>"Petr"); Після виконання функції asort($FIO) наш масив буде перетворений таким чином: array("Ivanov"=> "Alexandr","Sidorov"=>"Nikolay","Petrov"=>"Petr"); Сортування масивів по ключах. Функції ksort() і krsort() Для сортування по ключах використовується функція ksort(). При цьому сортування здійснюється за такими ж принципами, що і функцією asort(), тільки не по значенню, а по ключах. Якщо передати їй наш масив $по, то ми отримаємо такий результат: array("Ivanov"=>"Alexandr","Petrov"=>"Petr","Sidorov"=>"Nikolay"); Порівняєте отриманий результат з результатом функції asort(). Для сортування ключів в зворотному порядку використовується функція krsort(). Сортування списку Все чотири розглянуті функції не розривають пари ключ=>значення. Це дуже важливо. Наприклад, у нас є невеликий список: $People = array("Ivanov"=>"555-6677","Sidorov"=>"345-4567","Petrov"=>"777-5653"); Після сортування масиву по ключах наш список виглядатиме так: $People = array("Ivanov"=>"555-6677","Petrov"=>"777-5653","Sidorov"=>"345-4567"); Зверніть увагу: пари ключ=>значение не розірвані, тобто у Петрова як і раніше номер телефону 777-5653, а у Сидорова — 345-4567. Якби пари були розірвані, номер телефону у Петрова був 345-4567, а у Сидорова — 777-5653, тобто, навпаки. Якщо вам потрібно відсортувати список, то найзручніше використовувати функцію sort(). Дана функція розриває пари ключ=>значение, тому її потрібно використовувати тільки для списків. Хай у нас є список: $List = array("Name","Address","Email"); Якщо вивести список у вигляді ключ=>значение, то ми отримаємо наступний результат: => Name => Address => Email Після сортування sort(SList) у нас вийде наступний список: => Address => Email => Name Початковий зв'язок між парами ключ=>значение був розірваний. Виникне якраз та ситуація, яку ми розглядали на прикладі з номерами телефонів. Перевертання масиву. Функція array_reverse() Для перевертання масиву використовується функція array_reverse(). Ця функція повертає масив, елементи якого слідують в зворотному порядку щодо початкового масиву. Наприклад, щоб відсортувати масив в зворотному порядку, можна використовувати функцію arsort(), а можна упорядкувати масив в прямому порядку функцією asort() і перевернути його функцією array_reverse(). Примітка. Якщо вам потрібно відсортувати масив в зворотному алфавітному порядку, я рекомендую використовувати функцію arsort() — вона працює швидше, ніж послідовність функцій asort() і array_reverse(). Перемішування списків. Функція shuffle() Функція shuffle(), так би мовити, «перемішує» список, тобто значення результуючого списку будуть розміщені у випадковому порядку. Дана функція повністю змінює масив, тому її можна використовувати тільки для списків. Перед викликом цієї функції потрібно ініціалізувати генератор випадкових чисел, інакше при кожному новому виклику функції shuffle() результат перемішування буде один і той же (при перемішуванні одного і того ж масиву). Для ініціалізації генератора випадкових чисел використовується функція mt_srand(): mt_srand{time()*100000); shuffle($Arr); Примітка. Для генерування випадкових чисел рекомендується використовувати функції mt_rand() і mt_srand() замість стандартних rand() і srand(), оскільки перші забезпечують кращу якість випадкових чисел, що генеруються. Призначене для користувача сортування масивів. Функції uksort(), uasort() і usort() Іноді потрібно відсортувати масив не в алфавітному порядку, а по складнішому критерію. Для цього використовуються функції uksort(), uasort() і usort() — призначені для користувача функції сортування масивів. Кожній функції потрібно передати два параметри: масив, який потрібно відсортувати; функцію порівняння, яка визначить, який з елементів масиву менше, а який більше, згідно нашому критерію. Функції порівняння потрібно передати два параметри — два елементи масиву (Sel_l і SeI_2). У свою чергу, функція повинна повернути одне з трьох значень: -1, якщо $е1_К$е1_2; якщо $е1_1=$е1_2; якщо $е1_К$е1_2. Припустимо, що у нас є список файлів і каталогів — $FilesNDirs. Нам потрібно вивести спочатку каталоги, а потім файли. Порівняння проводитиметься функцією Cmp, яку ми самі і напишемо (див. лістинг 7.1). Докладніше про створення і використання призначених для користувача функцій ви можете прочитати в гл. 8. Лістинг 7.1. Функція порівняння Cmp function cmp($el_l,$el_2) { // Спочатку каталог, а потім - файл if (is_dir($el_l) && !is_dir(Sel_2)) return -1; if (!is_dir($el_l) && is_dir($el_2)) return 1; // Порівнюємо за абеткою if($el_K$el_2) return -1; elseif($el_l>$el_2) return 1; else return 0; } Тепер викличемо функцію uasort(): uasort ($FilesNDirs,"cmp"); Примітка. В даному випадку ми використовуємо uasort() тому, що у нас простій список, а не асоціативний масив. У останньому випадку потрібно було використовувати функцію uksort(). Функція uasort() аналогічна функції uksort(), тільки вона сортує не ключі, як uksort, а значення. Обидві функції — uksort() і uasort() — зберігають пари ключ=>значение. Функція usort() схожа на функцію sort(), але критерій сортування задається призначеною для користувача функцією, яка передається як другий параметр. Ще одна важлива особливість цієї функції полягає в тому, що вона не зберігає пару ключ=>значение, тому придатна лише для сортування списків, але її у жодному випадку не потрібно використовувати для сортування асоціативних масивів.