What's the difference between closure paramete

2019-03-23 09:19发布

问题:

This has got me very confused and I can't seem to find an answer to this question. A clear and simple clarification would be nice.

回答1:

A closure is a function that is evaluated in its own environment, which has one or more bound variables that can be accessed when the function is called. They come from the functional programming world, where there are a number of concepts in play. Closures are like lambda functions, but smarter in the sense that they have the ability to interact with variables from the outside environment of where the closure is defined.

The use() keyword let's you import variables from outside the function environment, inside the function. Variables to be imported from the outside environment are specified in the use clause of the closure function definition. By default, they are passed by value. So let's say the function has no parameters, but you wan't it to use a variable you already have.

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

This is useful when you need to create a function what must be used as callback somewhere else, and can only have defined parameters. The use() keyword let's you use other variables in addition to the ones you pass as function arguements. For example on the php.net example: http://php.net/manual/en/functions.anonymous.php

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 must only have two parameters, because array_walk will only allow that much:

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

So what can we do? We call use() to add other variables that are not the $callback's scope, but in scope of the environment it is being called in.



回答2:

The use statement captures the variable at the time the closure function is created.

Regular function arguments capture the value when the function is called.

Note that I differentiated between variable and value there.

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

If there were a way to inspect the, um, "source code" of the $add5to function, it would look like this:

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

I guess you could kinda say the value of $leftNum got remembered by the closure function.

I want to further emphasize that the use statement allows you to maintain a reference to a variable, and not just a copy of the value that the variable had at some point. To clarify my idea: think of a variable as being a little box, which can contain a single value at any instant in time, and that value can be changed. And, you can make another variable point to that box, so that you can update the value in the box, or read the current value in it.

Normally, a local variable that is created within a function ceases to exist after the function returns. But, a closure function can maintain a reference to that variable, and cause that local variable to live on even after the function returns - and this is the true power of closure functions. It lets you mimic certain behaviors of a class (instance variables), with just a tiny bit of code.

Here's a more advanced example, that might take some deep thinking to understand the fine details of the behavior.

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.

You may have noticed I used the reference operator & in this example. If you're not yet familiar with them yet, just know that References are known to be hard to understand. Although, I hope this post mostly makes sense by itself.