2013-04-15 10 views
36

Czytam The Go Programming Language Specifications i znalazłam naprawdę nie rozumieli "()" po ciele Zamknięcie:Dlaczego dodać "()" po zamknięciu ciała w Golang?

W Function literals:

func (ch chan int) {ch < - ACK} (replyChan) `

W przykładzie Defer statements„s

// f returns 1 
func f() (result int) { 
    defer func() { 
     result++ 
    }() // why and how? 
    return 0 
} 

Nie mam jasności co do powodu dodania po zamknięciu obiektu & użycia "()", mam nadzieję, że ktoś może to wyjaśnić w jasny sposób.

Odpowiedz

54

Nie należy dodawać () po (tylko) zamknięciu w defer. Specyfikacje językowe dla mandatu defer statement, że jego "Wyrażenie" musi zawsze być wywołaniem funkcji.

I dlaczego tak się dzieje? To tak samo jak z każdej innej funkcji, w 'odroczyć' czy nie:

Rozważmy:

func f() int { return 42 } 

i

a := f 

vs

b := f() 

pierwsze wyrażenie RHS jest wartość funkcji. W drugiej wersji RHS jest wartością zwróconą przez funkcję - to jest wywołanie funkcji.

Więc jest semantyka:

defer f 

vs

defer f() 

wyjątkiem tego, że pierwsza wersja nie ma sensu w kontekście „odroczyć”, a więc specyfikacje wspomnieć, że to musi być drugą formą (tylko).

It's IMHO również łatwiej się uczyć z powodu ortogonalności z wyżej omówionym wywołaniem funkcji poza instrukcją "odroczyć".

Należy również zauważyć, że wywołanie funkcji to nie tylko fn-expr, a następnie (), ale lista wyrażeń znajduje się zwykle w nawiasie (w tym pusta lista). Jest duża różnica pomiędzy:

for i := range whatever { 
     defer func() { fmt. Println(i) }() 
} 

i

for i := range whatever { 
     defer func(n int) { fmt. Println(n) }(i) 
} 

Pierwsza wersja drukuje wartość „i” w momencie, gdy zamknięcie wykonuje, drugi drukuje wartość „i” w momencie wykonania polecenia odroczenia .

+1

Dostałem twój punkt, więc w 'func (ch chan int) {ch <- ACK} (replyChan)', 'func (ch chan int) {ch <- ACK}' oznacza definicję zamknięcia, '(replyChan) 'oznacza" wykonaj to zamknięcie za pomocą parametru 'replyChan'". –

+0

Kolejne pytanie z twojego ostatniego przykładu: jaka jest różnica między "w momencie, kiedy zamknięcie się wykonuje" i "w momencie, kiedy została wykonana instrukcja odroczenia"? Jeśli mam 'return' po' for {} ', oba te 2' odroczenia 'powinny zostać wykonane przed' return' w tym samym czasie, czyż nie? –

+0

@ReckHou: Uruchom http://play.golang.org/p/Orm-0EaBY6 i spójrz na dane wyjściowe – zzzz

13

Odniesienia

The Go Programming Language Specification

Function types

typ funkcji oznacza zbiór wszystkich funkcji, z tych samych typem i wynikowych.

FunctionType = "func" Signature . 
Signature  = Parameters [ Result ] . 
Result   = Parameters | Type . 
Parameters  = "(" [ ParameterList [ "," ] ] ")" . 
ParameterList = ParameterDecl { "," ParameterDecl } . 
ParameterDecl = [ IdentifierList ] [ "..." ] Type . 

Function declarations

Oświadczenie funkcja wiąże identyfikator, nazwę funkcji, do funkcji.

FunctionDecl = "func" FunctionName Signature [ Body ] . 
FunctionName = identifier . 
Body   = Block . 

Function literals

Funkcja dosłownym oznacza anonimową funkcję. Składa się ze specyfikacji typu funkcji i treści funkcji.

FunctionLit = FunctionType Body . 

literały funkcyjne są zamknięcia: mogą one odnosić się do zmiennych zdefiniowanych w otaczającą funkcyjnego. Zmienne te są następnie dzielone między funkcję otaczającą i literalną funkcji, i zachowują się tak długo, jak są dostępne.

Literał funkcji można przypisać do zmiennej lub wywołać bezpośrednio.

Calls

Biorąc wyrazem f funkcji typu F,

f(a1, a2, … an) 

rozmowy f z argumentami a1, a2, … an.

W wywołaniu funkcji wartość funkcji i argumenty są obliczane w zwykłej kolejności według . Po ich ocenie parametry wywołania są przekazywane wartości do funkcji, a wywoływana funkcja zaczyna się od wykonania . Parametry zwracane funkcji są przekazywane z powrotem do wywołującej funkcji o wartości , gdy funkcja zwraca.

Defer statements

A „defer” oświadczenie wywołuje funkcję, której wykonanie jest odroczone do momentu okolicznych powraca funkcyjnych.

DeferStmt = "defer" Expression . 

Wyrażenie musi być funkcją lub wywołaniem metody. Za każdym razem, gdy wykonywana jest instrukcja "defer", wartość funkcji i parametry połączenia są obliczane jak zwykle i zapisywane od nowa, ale faktyczna funkcja nie jest wywoływana.Zamiast tego, odroczone połączenia są wykonywane w kolejności LIFO bezpośrednio przed powrotem funkcji otaczającej, po zwróceniu wartości , jeśli takie istnieją, ale zanim zostaną zwrócone do dzwoniącego.

Ponieważ wciąż nie masz pojęcia, oto kolejna próba udzielenia odpowiedzi na Twoje pytanie.

W kontekście twojego pytania, () jest operatorem wywoływania funkcji.

Na przykład, funkcja dosłownym

func(i int) int { return 42 * i } 

oznacza anonimową funkcję.

Funkcja dosłownym następnie () funkcji operatora wywołania

func(i int) int { return 42 * i }(7) 

oznacza anonimową funkcję, która jest następnie wywoływany bezpośrednio.

Zwykle w wywołaniu funkcji wartość funkcji i argumenty są obliczane w zwykłej kolejności. Po ich ocenie parametry wywołania są przekazywane wartości do funkcji, a wywoływana funkcja rozpoczyna wykonywanie. Parametry zwracane przez funkcję są przekazywane z powrotem do funkcji wywołującej, gdy funkcja zwraca.

Jednak wywołanie funkcji za pomocą polecenia odroczenia jest przypadkiem specjalnym. Za każdym razem, gdy wykonywana jest instrukcja "odroczenia", wartość funkcji i parametry połączenia są obliczane jak zwykle i zapisywane od nowa, ale faktyczna funkcja nie jest wywoływana. Zamiast tego, odroczone połączenia są wykonywane w kolejności LIFO bezpośrednio przed powrotem funkcji otaczającej, po zwróceniu wartości, jeśli jakiekolwiek, zostały ocenione, ale zanim zostaną zwrócone do osoby dzwoniącej.

Wyrażenie instrukcji odroczenia musi być wywołaniem funkcji lub metody, które jest wywoływane bezpośrednio, a nie tylko literałem funkcji lub metody, który nie jest wywoływany bezpośrednio. W związku z tym po funkcji lub metodzie cyfrowej musi następować operator wywołania funkcji (), aby wyrażenie odroczenia było funkcją lub wywołaniem metody.

Oświadczenie Defer

defer func(i int) int { return 42 * i }(7) 

jest prawidłowy.

Oświadczenie Defer

defer func(i int) int { return 42 * i } 

jest nieprawidłowy: syntax error: argument to go/defer must be function call.

Powiązane problemy