В чем разница между параметрами замыкания и ключевым словом use?

18

Это очень смутило меня, и я не могу найти ответ на этот вопрос. Четкое и простое разъяснение было бы неплохо.

    
задан Seralize 21.05.2012 в 23:23
источник
  • Я думаю, что вы выбрали неправильный ответ –  Trix 03.06.2016 в 17:50

2 ответа

20

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

Ключевое слово use () позволяет импортировать переменные извне функциональной среды, внутри функции. Переменные, которые должны быть импортированы из внешней среды, указаны в разделе use определения функции closure. По умолчанию они передаются по значению. Допустим, у функции нет параметров, но вы не хотите использовать переменную, которая у вас уже есть.

$string = "Hello World!";
$closure = function() use ($string) { echo $string; };

Это полезно, когда вам нужно создать функцию, которая должна использоваться как обратный вызов где-то еще, и может иметь только определенные параметры. Ключевое слово use () позволяет вам использовать другие переменные в дополнение к тем, которые вы передаете в качестве аргументов функции. Например, на примере php.net: Ссылка

public function getTotal($tax)
    {
        $total = 0.00;

        $callback =
            function ($quantity, $product) use ($tax, &$total)
            {
                $pricePerItem = constant(__CLASS__ . "::PRICE_" .
                    strtoupper($product));
                $total += ($pricePerItem * $quantity) * ($tax + 1.0);
            };

        array_walk($this->products, $callback);
        return round($total, 2);
    }

$ callback должен иметь только два параметра, потому что array_walk будет позволять только это:

Typically, funcname takes on two parameters. The array parameter's value being the first, and the key/index second.

Итак, что мы можем сделать? Мы вызываем use() , чтобы добавить другие переменные, которые не входят в область действия $ callback, но в контексте среды, в которую он вызывается.

    
ответ дан Stanislav Palatnik 21.05.2012 в 23:33
  • Это все еще не имеет смысла для меня. Как он отличается от $ clos = function ($ string) {echo $ string; } ;? –  Seralize 21.05.2012 в 23:41
  • @Seralize: Извините, обновил мой пример. В принципе, некоторые функции в PHP позволяют вызвать обратные вызовы (например, array_walk, array_map и т. Д., Которые имеют предопределенные параметры). В этих случаях вы можете вызвать use () для добавления других переменных, которые в противном случае не могли бы использоваться в обратном вызове. –  Stanislav Palatnik 21.05.2012 в 23:44
  • Это имеет смысл. Таким образом, ключевое слово use () - это просто способ импортировать переменные в замыкание без испорчения «потока параметров», поскольку параметры, которые не заданы, будут давать ошибку? –  Seralize 21.05.2012 в 23:46
  • @Seralize: Да. Это позволит вам добавить больше контекста к функции без изменения параметров. –  Stanislav Palatnik 21.05.2012 в 23:48
  • И то, что сказал @chris! –  Stanislav Palatnik 21.05.2012 в 23:58
Показать остальные комментарии
24

use statement захватывает переменную во время создания функции закрытия .

Обычные аргументы функции фиксируют значение , когда функция вызывается .

Обратите внимание, что я различал variable и value там.

function makeAnAdder($leftNum) {
    // Notice that *each time* this makeAnAdder function gets called, we 
    // create and then return a brand new closure function.
    $closureFunc = function($rightNum) use ($leftNum) {
        return $leftNum + $rightNum;
    };

    return $closureFunc;
}

$add5to = makeAnAdder(5);
$add7to = makeAnAdder(7);

echo $add5to(10); // 15
echo $add7to(1); // 8

Если бы был способ проверить "исходный код" функции $add5to , это выглядело бы так:

function($rightNum) {
    return 5 + $rightNum;
}

Полагаю, можно сказать, что значение $leftNum запомнилось функцией закрытия.

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

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

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

function makeBankAccount() {
    // Each time this makeBankAccount func is called, a new, totally
    // independent local variable named $balance is created.
    $balance = 0;

    // Also, on each call we create 2 new closure functions, $modifyBalance, and $getBalance
    // which will hold a reference to the $balance variable even after makeBankAccount returns.
    $modifyBalance = function($amount) use (&$balance) {
        $balance += $amount;
    };

    $getBalance = function() use (&$balance) {
        return $balance;
    };

    // return both closure functions.
    return ['modifyBalance' => $modifyBalance, 'getBalance' => $getBalance];
}

// Let's prove that bank1 works by adding 5 to the balance by using the first
// function, then using the other function to get the balance
// from the same internal variable.
$bank1 = makeBankAccount();
$bank1['modifyBalance'](5);
echo $bank1['getBalance'](); // 5 - it works.

// Now let's make another bank to prove that it has it's own independent internal $balance variable.
$bank2 = makeBankAccount();
$bank2['modifyBalance'](10);
echo $bank2['getBalance'](); // 10 - as expected. It would have printed 15 if bank2 shared a variable with bank1.

// Let's test bank1 one more time to be sure that bank2 didn't mess with it.
echo $bank1['getBalance'](); // 5 - still 5, as expected.

Возможно, вы заметили, что в этом примере я использовал оператор ссылки & . Если вы еще не знакомы с ними, просто знайте, что ссылки, как известно, трудно понять. Хотя, я надеюсь, что этот пост в основном имеет смысл сам по себе.

    
ответ дан goat 21.05.2012 в 23:51
  • Это хороший момент, который вы получили там. Мне потребовалось некоторое время, чтобы понять, как был вызван параметр $ rightnum, но теперь это имеет смысл. –  Seralize 22.05.2012 в 00:04
  • Я, некоторые из этих вещей действительно трудно понять, потому что есть так много точек косвенности и оценки. Я добавлю пояснения, которые, надеюсь, помогут. –  goat 22.05.2012 в 00:07
  • Этот ответ заслуживает того, чтобы быть принятым ответом –  Trix 03.06.2016 в 17:49