Pytanie w sprawie php, anonymous-function, closures – Jaka jest różnica między parametrami zamknięcia a słowem kluczowym „użyj”?

18

To bardzo mnie zdezorientowało i nie mogę znaleźć odpowiedzi na to pytanie. Jasne i proste wyjaśnienie byłoby miłe.

Myślę, że wybrałeś złą odpowiedź Pmpr

Twoja odpowiedź

2   odpowiedź
21

która jest oceniana w swoim własnym środowisku, która ma jedną lub więcej zmiennych powiązanych, do których można uzyskać dostęp po wywołaniu funkcji. Pochodzą ze świata programowania funkcjonalnego, w którym występuje wiele pojęć. Zamknięcia są jak funkcje lambda, ale inteligentniejsze w tym sensie, że mają możliwość interakcji ze zmiennymi ze środowiska zewnętrznego, w którym zdefiniowane jest zamknięcie.

Słowo kluczowe use () pozwala importować zmienne spoza środowiska funkcji wewnątrz funkcji. Zmienne do zaimportowania ze środowiska zewnętrznego są określone w klauzuli use definicji funkcji zamknięcia. Domyślnie są przekazywane przez wartość. Powiedzmy, że funkcja nie ma parametrów, ale nie chcesz używać zmiennej, którą już posiadasz.

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

Jest to przydatne, gdy trzeba utworzyć funkcję, która musi być użyta jako wywołanie zwrotne w innym miejscu i może mieć tylko zdefiniowane parametry. Słowo kluczowe use () pozwala nam używać innych zmiennych oprócz tych, które przekazujesz jako argumenty funkcji. Na przykład w przykładzie php.net: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 musi mieć tylko dwa parametry, ponieważ array_walk pozwoli tylko tyle:

Zazwyczaj funcname przyjmuje dwa parametry. Wartość parametru tablicy jest pierwsza, a klucz / indeks drugi.

Więc co możemy zrobić? Nazywamyuse() aby dodać inne zmienne, które nie są zakresem $ callback, ale w zakresie środowiska, w którym jest wywoływane.

To nadal nie ma dla mnie sensu. Czym się różni od tego$closure = function($string) { echo $string; };? Seralize
@Seralize: To cały punkt azamknięcie. Nie ma to nic wspólnego z „przepływem parametrów” lub czymkolwiek. To, co później podałeś, to funkcja, która zawsze wypisuje swój argument, co nie jest interesujące. Z wyrażeniem z wcześniejszego możemy stworzyć funkcję, która wypisuje „foo” lub funkcję, która wypisuje „bar” lub cokolwiek innego; i robimy to bez faktycznego pisania różnych funkcji; że jeden wiersz kodu, za każdym razem, gdy zostanie uruchomiony, utworzy funkcję z nowym zachowaniem (w zależności od wartości przechwyconej zmiennej) - tak jakbyś ręcznie napisał dla nich osobne funkcje. To jest różnica. newacct
W pewnym sensie wydaje się to bezcelowe, ale w innym zawsze będą takie sytuacje, w których takie małe rzeczy mogą pomóc rozwiązać problem. Dzięki za wyjaśnienie! Seralize
@Seralize: Tak. Pozwala to dodać więcej kontekstu do funkcji bez zmiany parametrów. Stanislav Palatnik
25

use statement oddajezmienna w tym czasiefunkcja zamykania jest tworzona.

Regularne argumenty funkcji przechwytująwartość kiedy funkcja jestnazywa.

Zauważ, że rozróżniłem międzyvariable ivalue tam.

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

Gdyby był sposób na sprawdzenie, „hm” „kodu źródłowego”$add5to funkcja wyglądałaby tak:

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

Myślę, że mógłbyś powiedzieć wartość$leftNum został zapamiętany przez funkcję zamknięcia.

Chcę jeszcze bardziej podkreślić, żeuse statement pozwala na utrzymaniereference do azmienna, a nie tylko kopiawartość że zmienna miała w pewnym momencie. Aby wyjaśnić mój pomysł: pomyśl o zmiennej jako o małym pudełku, które może zawierać pojedynczą wartość w dowolnej chwili w czasie, a wartość tę można zmienić. I możesz utworzyć inny punkt zmiennej do tego pola, abyś mógł zaktualizować wartość w polu lub odczytać bieżącą wartość w tym polu.

Normalnie zmienna lokalna tworzona w funkcji przestaje istnieć po zwróceniu funkcji. Jednak funkcja zamknięcia może utrzymywać odniesienie do tej zmiennej i powodować, że ta lokalna zmienna będzie działać nawet po zwróceniu funkcji - i to jest prawdziwa moc funkcji zamykania. Pozwala naśladować pewne zachowania klasy (zmienne instancji) za pomocą niewielkiej ilości kodu.

Oto bardziej zaawansowany przykład, który może wymagać głębokiego przemyślenia, aby zrozumieć drobne szczegóły zachowania.

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.

Być może zauważyłeś, że użyłemoperator referencyjny & w tym przykładzie. Jeśli jeszcze ich nie znasz, po prostu wiedz, że trudno jest zrozumieć odniesienia. Mam jednak nadzieję, że ten post sam w sobie ma sens.

Tak, niektóre z tych rzeczy są naprawdę trudne do zrozumienia, ponieważ jest tak wiele punktów orientacji i oceny. Dodam wyjaśnienie, które, mam nadzieję, pomoże. goat
Ta odpowiedź zasługuje na zaakceptowaną odpowiedź Pmpr
To dobry punkt, że tam dotarłeś. Zajęło mi trochę czasu, aby zrozumieć, jak$rightnum parametr został wywołany, ale teraz ma sens. Seralize

Powiązane pytania