Лямбда-функции и замыкания

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

Чтобы выполнить задания, вам потребуются следующие программы:

Программное обеспечение или ресурс Требуемая версия
Denwer (PHP5.3) 3 +
Notepad, или любой текстовый редактор
Mozilla Firefox 3.6 +

Примечания:

  • Мы предполагаем, что у вас есть базовые знания PHP.

Что это за функции такие: лямбда и замыкание?

В PHP 5.3 есть множество интересных возможностей, которые приближают его синтаксис к таким языкам программирования как: JavaScript, Python, Ruby и т.д. И, что более важно, эти возможности полезны и стали популярными.

Лямбда-функции и замыкания — это программные объекты, не обладающие именем, они реализуются там, где потребовалась их помощь. Обратите внимание, лямбда-функции не захватывают контекст, а замыкания предназначены для его захвата. Сразу скажем, обе эти функций в PHP по типу соответствуют классу Closure, и указанные выше отличия не принципиальны для практического использования в PHP. В практике вы заметите только то, что от класса Closure ни наследоваться нельзя, ни экземпляры создавать. Его ввели только для внутреннего использования — для типизации.

Лямбда-функции

Цель:

  • Научится использовать лямбда функции в качестве параметров других функций

Лямбда-функции, оказывается, очень полезны для “здоровья” некоторых функций, особенно тех, которые требуют в качестве параметра функцию.

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

Представьте, что нам всего в одном месте программы нужно отсортировать массив объектов по некоторому полю, нам не нужно объявлять отдельную функцию, мы воспользуемся лямбда-функцией. В Листинге №1 показано как использовать лямбда-функцию для сортировки массива.

Листинг №1 (строки лямбда-функции подсвечены):

<?php
class naturalNumber {
	private $number;
	public function setNumber($number) {
		$this->number = $number;
	}
	public function getNumber() {
		return $this->number;
	}
}

$aNumber = array();

for($i = 0; $i < 10; $i++) {
	$oNumber = new naturalNumber();
	$oNumber->setNumber(rand(1, 10));
	$aNumber[] = $oNumber;
}

echo "До сортировки:<pre>";
print_r($aNumber);
echo "</pre>";

usort($aNumber,
	function($oFirst, $oSecond) {
		if($oFirst->getNumber() < $oSecond->getNumber()) {
			return -1;
		}
		$result = (
			$oFirst->getNumber() == $oSecond->getNumber()
		) ? 0 : 1;
		return $result;
	}
);

echo "После сортировки:<pre>";
print_r($aNumber);
echo "</pre>";
?>

В этом примере происходит заполнение массива $aNumber объектами, которые создаются на основе класса naturalNumber. В каждом объекте хранится случайное число, доступ к которому осуществляется через функции getNumber() и setNumber(). Затем происходит сортировка массива с использованием лямбда-функции. Для usort() необходимо, чтобы вторым параметром была указана функция с двумя аргументами, каждый из которых должен соответствовать по типу элементам сортируемого массива. После заполнения массива и в конце программы происходит вывод массива.

Замыкания

Цель:

  • Использовать контекст функции

Замыкания тоже «вкусны» по-своему, оттенки их «вкуса» можно заметить не всегда. Их применение в коде похоже по стилю на JavaScript. Замыкания, кроме того что они используют переменные контекста, в котором они определены, обладают ещё одним замечательным свойством — их время жизни может быть больше, чем у той функции, в которой их определили.

Для примера захвата значений переменных, возьмём суммирование массива и возвращение результата в контекст программы. Если точнее, захватываться будет ссылка на переменную, в которой будет происходить суммирование. Для полного счастья, суммировать будем рекурсивно по массиву :), побываем во всех его уголках. Пример программы показан в Листинге №2:

Листинг №2 (строки замыкания подсвечены):

<?php
$aText =
	array(
		array(
		 'JavaScript',
		 'это язык программирования',
		 array(
			 'использующийся в веб-приложениях',
			 array(
				 'на стороне клиента.'
			 )
		 ),
		),
	);

$text = '';

array_walk_recursive(
	$aText,
	function($item, $key) use(&$text) {
		 $text .= ' ' . $item;
	}
);

echo "$text<br />";
?>

В этом примере происходит создание строки из многомерного массива, путём склеивания всех его элементов через пробел. Стандартно для склеивания используется функция implode(), но здесь не тот случай — она не склеивает многомерные массивы. Может для кого-то этот пример будет кстати.

Существует особенность, которую нужно учитывать, когда вы используете замыкание в методе (метод — функция, член класса). Оказывается оно (замыкание) не захватывает внутреннюю переменную $this (отвечающую за текущий экземпляр). Для того, чтобы её всё-таки передать в замыкание, нужно использовать локальную переменную, например вот так $self = $this, а затем уже передавать в замыкание use($self).

Способов применения этим функциям можно найти массу. К примеру, можно создавать проекты в стиле JavaScript, можно продлевать жизнь параметрам и переменным и ещё многое. Будем надеяться, что в PHP 6 этот механизм станет более похож на то, что уже есть в других языках.

Есть вопросы? Добро пожаловать в коментарии.

Понравилась статья? Посоветуйте другу

Количество коментариев: 4

  1. Хочется верить в PHP6, а то пока со статическими методами классов проблема. Разработчики молчат, хотя баг вывешен им…

    • Дмитрий Артанов Дмитрий Артанов

      Посмотрел ваш сайт, красиво ❗
      А вот на счёт обращения через self внутри замыкания, я думаю это не баг. Просто эта фича не заложена пока в PHP. Вот и в документации пишут — если чего даёте на съедение замыканию, то только переменные и, пожалуйста, через use().

  2. Задача, которая натолкнула на использование лямбда функций, была банальной — плагин к wordpress. class использовал с целью оформления namespace, но использовал статические методы класса. Ну и статические методы класса и пришлось регистрировать как «фильтры» и «actions» в терминологии wordpress. Тут и напрашивались лямбда функции. Но они (лямбда фукнции, определённые в коде статического метода класса) не относятся к методами класса (то есть видят только public члены класса и не могут неявно использовать self). Может есть уже и иное решения (ну, кроме того, что было до 5.3, «классику» знаю, но она избыточна и не особо читаема в ряде случаев)?

    • Дмитрий Артанов Дмитрий Артанов

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

Добавить комментарий



[ Ctrl + Enter ]