Замыкания. Стрелочные функции

Замыкания

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

Вначале рассмотрим анонимную функцию, которая пытается использовать некоторую внешнюю переменную:

<?php
$number = 89;
 
$showNumber = function()
{
    echo $number;
};
 
$showNumber();
?>

Анонимная функиция, которая представлена переменной $showNumber, пытается обратиться к внешней переменной $number. Однако при выполнении скрипта мы увидим в браузере сообщение об ошибке:

Warning: Undefined variable $number in C:\localhost\hello.php on line 13

То есть по умолчанию переменной $number для анонимной функции не существует. Из данной сиутации мы можем выйти, использовав оператор global или массив $GLOBALS, которые рассматриваются в одной из последующих тем. Тем не менее замыкания также позволяют решить эту проблему. Так, трансформируем функцию в замыкание:

<?php
$number = 89;
 
$showNumber = function() use($number)
{
    echo $number;
};
 
$showNumber();
?>

Выражение use() получает внешние переменные, которые анонимная функция собирается использовать. И теперь при ее выполении браузер выведет значение переменной $number.

Подобным образом функция-замыкание может захватывать и большее количество внешних переменных, а также как и другие функции применять параметры:

$a = 8; 
$b = 10;
 
$closure = function($c) use($a, $b)
{
    return $a + $b + $c;
};
 
$result = $closure(22); // 40
echo $result;

Пример применения замыканий

Может возникнуть закономерный вопрос, зачем это делать, если переменные можно передать в сам вызов.

Замыкания полезны в тех случаях, когда функция определяется в одном месте, а используется в совершенно другом. Замыкание позволяет не таскать за собой гору переменных. А в некоторых ситуациях без них просто не обойтись. Вспомните функцию without() из пакета Funct. Эта функция принимает на вход массив и значение, а возвращает новый массив полученный фильтрацией старого по переданному значению. Его реализация, построенная на функциях высшего порядка, подразумевает фильтрацию. Сложность возникает при описании предиката, ведь внутри анонимной функции нужно сравнивать текущее значение и переданный элемент. Замыкание позволяет решить эту задачу просто.

<?php

function without(array $items, $value)
{
    $filtered = array_filter($items, function ($item) use ($value) {
        return $item !== $value;
    });
    // Сбрасываем ключи
    return array_values($filtered);
}

without([3, 4, 10, 4, 'true'], 4); // [3, 10, 'true']

Без добавления use ($value) ничего не получится – $value не виден внутри анонимной функции.

Что такое стрелочные функции

Стрелочные функции (arrow function) позволяют упростить запись анонимных функций, которые возвращают некоторое значение. И при этом стрелочные функции автоматически имеют доступ к переменным из внешнего окружения.

Стрелочная функция определяется с помощью оператора fn:

fn(параметры) => действия;

После оператора fn в скобках идет список параметров. Затем указывается оператор =>. А после него располагаются действия функции, которые возвращают некоторый результат.

Пример стрелочной функции

$a = 8; 
$b = 10;
 
$closure = fn($c) => $a + $b + $c;
 
$result = $closure(22); // 40

В данном случае определение стрелочной функции. Фактически будет аналогично:

$closure = function($c) use($a, $b)
{
    return $a + $b + $c;
};

Только в отличие от стандартных анонимных функций стрелочный функции предоставляют более лаконичный синтаксис.

Использование в качестве параметра фкнкции

function sum($numbers, $condition)
{
    $result = 0;
    foreach($numbers as $number){
        if($condition($number))
        {
            $result += $number; 
        }
    }
    return $result;
}
 
$myNumbers = [-2, -1, 0, 1, 2, 3, 4, 5];
 
$positiveSum = sum($myNumbers, fn($n)=>$n > 0);
$evenSum = sum($myNumbers, fn($n) => $n % 2 === 0);
echo "Сумма положительных чисел: $positiveSum <br /> Сумма четных чисел: $evenSum";

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *