2016-01-10 7 views
12

Uczę się Swifta i uważam to za dziwne, dlaczego podczas wywoływania funkcji nazwa pierwszego parametru nie jest wymagana.Dlaczego nazwa pierwszego parametru funkcji w Swift nie jest wymagana podczas wywoływania?

func say(greeting: String, toName: String) { 
    print("\greeting), \(toName)!") 
} 

say("Goodbye", toName: "Hollywood") // <-- why is there no "greeting" required? 
+0

, więc pytasz, dlaczego twórcy tego języka zdecydowali się pójść w ten * sposób? – luk2302

+0

Myślę, że nie jest spójny mieć pierwszy parametr bez nazwy zewnętrznej, a pozostałe parametry mają nazwy zewnętrzne, myślę, że powinny one lepiej mieć wszystkie parametry używać nazw zewnętrznych dla lepszej czytelności. Myślę, że lepiej zmienić ten projekt i mieć wszystkie parametry z zewnętrznymi nazwami. –

Odpowiedz

10

Jak mówili inni, jest to kwestia stylu pochodzącego z Objective-C.

Aby zrozumieć dlaczego ktoś chciałby tego stylu, należy rozważyć modyfikację swojej przykład:

func say(greeting: String) { 
    print("\(greeting)") 
} 

który można by nazwać tak:

say("Hello!") 

Jeśli spojrzeć na nazwiska ty "Używam, prawdopodobnie brakuje niektórych informacji. Z funkcją o nazwie say(), możesz rozsądnie myśleć, że jest to funkcja pozwalająca ci cokolwiek powiedzieć. Ale kiedy patrzysz na nazwę swojego parametru, jest całkiem jasne, że jest to funkcja do wypowiadania pozdrowienia, a nie do mówienia czegokolwiek.

Więc Objective-C woleliby, aby napisać to tak:

func sayGreeting(greeting: String) { 
    print("\(greeting)") 
} 

który można by nazwać tak:

sayGreeting("Hello!") 

i to jest teraz jasne, że mówią na powitanie. Innymi słowy, sama nazwa funkcji lepiej opisuje to, co robisz. Z tego powodu, sayGreeting("Hello!") jest lepszym rozwiązaniem niż say(greeting: "Hello!"), ponieważ kluczowa rzecz, jaką pełni dana funkcja, powinna być opisana przez jej nazwę, a nie relegowana do nazwy parametru i mająca znaczenie drugorzędne.

Ale to uzasadnienie działa tylko dla pierwszego argumentu. Załóżmy, że chcesz dodać nazwę, tak jak to zrobiłeś. W języku jak C, gdzie masz żadnych zewnętrznych nazw parametrów w ogóle, można napisać:

void sayGreetingToName(char * greeting, char * person) { ... 

i nazywają to lubią:

sayGreetingToName("Hello", "Dave"); 

która jest OK, ale zaczyna się rozpadają szybko, gdy masz przeładowany funkcji lub wartości domyślne, których nie masz w C. Jeśli chciałeś napisać:

func sayGreetingToName(greeting: String, name: String? = nil) { 
    if let name = name { 
     print("\(greeting), \(name)!") 
    } 
    else { 
     print("\(greeting)!") 
    } 
} 

następnie nazywając ją jako:

sayGreetingToName("Hello", "Dave") 

wyglądałby w zasadzie OK, ale:

sayGreetingToName("Hello") 

wygląda śmiesznie, bo nazwa funkcji mówi, że jesteś dostarczenie imię, ale nie jesteś.

Zamiast więc, jeśli napiszesz:

func sayGreeting(greeting: String, toName: String? = nil) { 
    if let name = toName { 
     print("\(greeting), \(name)!") 
    } 
    else { 
     print("\(greeting)!") 
    } 
} 

można nazwać to na dwa sposoby:

sayGreeting("Hello") 
sayGreeting("Hello", toName: "Dave") 

i wszystko wygląda zupełnie jasne.

Podsumowując, idea tego stylu pisania jest taka, że ​​sama nazwa funkcji powinna zawierać wszelkie informacje zawarte w nazwie pierwszego parametru, ale nie ma sensu rozszerzać tego na kolejne parametry. Domyślnie pierwszy nie ma nazwy zewnętrznej, ale reszta nie. Funkcja polega na tym, że przez cały czas wypowiadane jest powitanie, więc powinno ono być wpisane w nazwę funkcji (a zatem nie powinno być powielane przez naciskanie na zewnętrzną nazwę pierwszego parametru), ale może, ale nie musi, o tym mówić. do konkretnej nazwy, aby informacje mogły być w nazwie funkcji nie być .

pozwala również na zasadzie odczytu wywołanie funkcji, jak gdyby był angielski, ponieważ nazwy i parametry są teraz mniej więcej w odpowiedniej kolejności, aby to zrobić:

sayGreeting("Hello", toName: "Dave") 

Say (the) powitanie, "Hello", to (osoba z) imieniem "Dave"

Jest to całkiem fajny styl, kiedy już się przyzwyczaisz.

2

Jest to domyślne zachowanie dla funkcji w swift, pomijając nazwę zewnętrzną pierwszego parametru funkcji.

domyślnie pierwszy parametr pomija swoją nazwę zewnętrznego, a drugie i kolejne parametry używać ich lokalną nazwę jako nazwą zewnętrznego.

Od Language Guide - Functions.

Uwaga jednak, że można dodać nazwę zewnętrznego również do pierwszego parametru funkcji, jeśli sobie tego życzą:

func foo(extBar bar: String, bar2: String) { 
    print(bar+bar2) 
} 

foo(extBar: "Hello", bar2: "World") 

Podobnie można powiedzieć 2nd (i tak dalej) parametry funkcji pominąć ich zewnętrzna nazwy, dodając _ przed nazwą wewnętrzną parametru w podpisie funkcji.

func foo2(bar: String, _ bar2: String) { 
    print(bar+bar2) 
} 

foo2("Hello", "World") 

jednak pamiętać, że dla inicjalizatorów nazwy zewnętrzne są obowiązkowe dla wszystkich parametrów funkcji, w tym pierwszym.

Jak z parametrów funkcji i sposób, parametry inicjalizacji może mają zarówno lokalną nazwę stosowania w ciele inicjatora i jest zewnętrzną nazwę do wykorzystania podczas wywoływania inicjatora.

Jednak inicjalizatory nie mają nazwy funkcji identyfikującej przed nawiasami w taki sposób, w jaki działają funkcje i metody. Dlatego też nazwy i typy parametrów inicjalizatora odgrywają szczególnie ważną rolę w określeniu, który inicjator powinien zostać wywołany. Z tego powodu, Swift udostępnia automatyczną nazwę zewnętrzną dla każdego parametru w inicjatorze, jeśli nie podajesz zewnętrznej nazwy .

Od Language Guide - Initialization.

Jako przykład rozważmy

struct Foo { 
    var bar : Int 

    init(extBar: Int) { 
     bar = extBar 
    } 
} 

var a = Foo(extBar: 1) 

Również w tym przypadku można jednoznacznie powiedzieć konstruktora pozwolić parametry pominąć ich nazwiska zewnętrznych

struct Foo2 { 
    var bar : Int 

    init(_ intBar: Int) { 
     bar = intBar 
    } 
} 

var a = Foo2(1) 
2

Jak @dfri już wspomniano, to po prostu był zdecydował w ten sposób.

pamiętać, że można łatwo dostosować to zachowanie jawnie wymagając nazwy parametrów przy wywołaniu i _ pominąć je:

func say (greeting greeting: String, _ toName: String){ 
    print("\(greeting), \(toName)!") 
} 

wyniki w konieczności nazwać poprzez

say(greeting: "Goodbye", "Hollywood") 

Alternatywnie

func say (greeting: String, _ toName: String){ 
    print("\(greeting), \(toName)!") 
} 

say("Goodbye", "Hollywood") 

lub

func say (greeting greeting: String, toName: String){ 
    print("\(greeting), \(toName)!") 
} 

say (greeting: "Goodbye", toName: "Hollywood") 
5

Chris Lattner mówi o tym dokładnie w What's New In Swift 2 Talk:

powrotem w dniach Swift 1.x to zachowanie stosowane tylko do metod. Funkcje wypisane poza klasami, strukturami lub enumami nie wymagały wywoływania w wywołaniu funkcji żadnego parametru (chyba że wymusiłeś to wyraźnie, używając zewnętrznej nazwy parametru w definicji funkcji).

Z powodu konwencji z Objective-C metody były często nazywane w taki sposób, że pierwszy parametr był już częścią nazwy metody.
Przykładami są: indexOf(_:) zamiast index(of:) lub charactersAtIndex(_:) zamiast charactersAt(index:).
W Objective-C będzie to napisane jako indexOf: i charactersAtIndex:. Nie ma nawiasów klamrowych do oddzielania nazwy funkcji od parametrów funkcji. Więc parametry były w zasadzie częścią nazwy funkcji.

Jak wspomniano wcześniej, to zachowanie dotyczyło tylko metod na początku. Spowodowało to zamieszanie wśród programistów, gdy dodawano nazwę zewnętrzną do pierwszego parametru, a gdy nie. Ostatecznie zachowanie zostało zmienione, tak że pierwszy parametr nie używa domyślnie nazwy wewnętrznej jako nazwy zewnętrznej, ale wszystkie z poniższych parametrów działają.
Spowodowało to bardziej spójne użycie zewnętrznych i wewnętrznych nazw parametrów. I takie zachowanie istnieje dzisiaj w Swift.

tl; dr Zachowanie jest pozostałością z Objective-C

+1

Po prostu bycie pedantycznym: to zachowanie nie jest pozostałością * od * objc per se, więcej resztek * od konwencji projektowania API używanych z * ObjC. Apple równie dobrze mógł zdecydować się na "say (greeting:" Hello ", toName:" World ")" domyślny i nadal miał fajną rzecz z historii Cocoa z witryn połączeń czytających jak angielskie zdania ... Ale jeśli tak zrobili że albo: a) wszystkie importowane metody ObjC wyglądałyby inaczej niż natywne metody Swift, lub b) musiałyby sprawić, że importer bezbłędnie zamapuje każdą metodę ObjC, jak np. 'sayGreeting: toName:' ​​na metodę Swift taką jak 'say (greeting: toName:) '. – rickster

Powiązane problemy