PHP: Функции

Функции высшего порядка

Пока мы рассмотрели только одну возможность объектов первого рода применительно к функциям — присвоение переменной. Самое интересное начинается, когда мы передаём одни функции в другие функции.

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

<?php

$users = [
    ['name' => 'Igor', 'age' => 19],
    ['name' => 'Danil', 'age' => 1],
    ['name' => 'Vovan', 'age' => 4],
    ['name' => 'Matvey', 'age' => 16],
];

При таких условиях функция sort становится абсолютно бесполезной, потому что она может сортировать только списки примитивных типов данных. Но выше я описал только лишь одну из тысяч возможных ситуаций. Мы можем захотеть сортировать по любому параметру (или даже по набору параметров) и в любом порядке. Сортировки нужны часто, и многие из них довольно сложны. Худшее, что можно начать делать — реализовывать функцию sort под каждую ситуацию. Так что же делать? Если покопаться в документации PHP, то можно обнаружить функцию usort. Её определение звучит так:

Сортирует массив по значениям, используя пользовательскую функцию для сравнения элементов

bool usort ( array &$array , callable $value_compare_func )

Эта функция сортирует элементы массива, используя для сравнения значений callback-функцию, предоставленную пользователем. Используйте эту функцию, если вам нужно отсортировать массив по какому-нибудь необычному признаку. Слово callback означает то, что наша задача — передать функцию (но не вызывать!), а вызывать её будет функция usort.

Общая идея состоит в том, что нам не нужно реализовывать алгоритм сортировки каждый раз для каждой ситуации, ведь он не меняется. Все, что меняется — элементы, которые сравниваются между собой в процессе сортировки. И функция usort делегирует взаимодействие с этими элементами вызывающему коду.

<?php

$cmp = function ($a, $b) {
  if ($a['age'] === $b['age']) {
    return 0;
  }
  return $a['age'] > $b['age'] ? 1 : -1;
};

usort($users, $cmp);

print_r($users);
// => [
//      ['name' => 'Danil', 'age' => 1],
//      ['name' => 'Vovan', 'age' => 4],
//      ['name' => 'Matvey', 'age' => 16],
//      ['name' => 'Igor', 'age' => 19],
//    ];

https://repl.it/@hexlet/php-functions-high-order-functions-usort

Функция usort выполняет всю работу по непосредственному перемещению элементов в массиве, но то, какой элемент больше или меньше, — зависит от вас. Достигается подобная схема за счёт той самой пользовательской функции, передающейся вторым параметром. Эта функция принимает на вход два параметра — usort отдаёт в неё два элемента, которые она сравнивает в данный момент. В нашем случае элементы — пользователи. Ваша задача — внутри этой функции посчитать, что больше или меньше, и сделать следующее. Если элементы равны, то вы должны вернуть 0, если первый элемент больше второго, то считается, что они отсортированы неправильно, и вы должны вернуть 1, иначе возвращается -1, а usort производит их сортировку.

Из кода выше видно, что внутри функции сравнение идёт по свойству age переданных пользователей. Нетрудно догадаться, что эта функция вызывается внутри usort множество раз (а именно на каждое сравнение). Как только она начнёт возвращать -1 для каждой пары элементов — сортировка завершена.

Код выше можно упростить воспользовавшись специальным оператором <=> (spaceship). Он возвращает 1, -1 или 0 в зависимости от того, как соотносятся его операнды, и работает так, как того ожидает сортировка:

<?php

3 <=> 5; // -1 (выражение слева от оператора "<=>" меньше выражения справа)
5 <=> 5; // 0 (выражения слева и справа равны)
5 <=> 3; // 1 (выражение слева больше выражения справа)

Тогда код сокращается до такого:

<?php

$cmp = fn($a, $b) => $a['age'] <=> $b['age'];
usort($users, $cmp);

Функция usort относится к так называемым функциям высшего порядка (high order functions). Функции высшего порядка — это функции, которые либо принимают, либо возвращают другие функции, либо делают все сразу. Такие функции, как правило, реализуют некий обобщённый алгоритм (например, сортировку), а ключевую часть логики делегируют вам через callback-функцию. Главный плюс от применения таких функций — серьёзное повышение коэффициента повторного использования кода.

В примере выше не обязательно создавать переменную для функции. Говоря откровенно, их вообще редко записывают в переменные. Типичное использование выглядит как прямая передача функции в функцию:

<?php

usort($users, fn($a, $b) => $a['age'] <=> $b['age']);

Потратьте немного времени, чтобы понять, где заканчивается одна функция и начинается другая. Подобный код мы начнём использовать уже со следующего урока.

Осталось рассмотреть то, как происходит вызов внутри. С точки зрения синтаксиса ничего нового не будет.

<?php

function say(callable $fn)
{
    echo $fn();
}

say(fn() => 'hi!'); // => hi!

Функция say делает вызов функции, находящейся внутри переменной $fn. В нашем примере функция возвращает строку, которая тут же выводится на экран.

Функции высшего порядка настолько удобны в большинстве языков, что практически целиком могут заменить использование тех же циклов. В следующих уроках мы рассмотрим три самые главные функции высшего порядка, которыми можно решать практически любые задачи.


Дополнительные материалы

  1. PHP The Right Way

<span class="translation_missing" title="translation missing: ru.web.courses.lessons.mentors.mentor_avatars">Mentor Avatars</span>

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.
Об обучении на Хекслете

Для полного доступа к курсу нужна профессиональная подписка

Профессиональная подписка откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
115
курсов
892
упражнения
2241
час теории
3196
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 115 курсов, 2000+ часов теории
  • 800 практических заданий в браузере
  • 250 000 студентов

Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг».

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Rambler
Логотип компании Bookmate
Логотип компании Botmother

Есть вопрос или хотите участвовать в обсуждении?

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг».