2016-01-10 13 views
12

Sto imparando Swift e trovo strano il motivo per cui quando si chiama una funzione, il nome del primo parametro non è richiesto.Perché il nome del primo parametro di una funzione in Swift non è richiesto durante la chiamata?

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

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

quindi stai chiedendo perché i creatori della lingua hanno deciso di andare * così * in questo modo? – luk2302

+0

Penso che non sia coerente avere il primo parametro senza nome esterno e il resto dei parametri hanno nomi esterni, penso che dovrebbero essere meglio che tutti i parametri utilizzino nomi esterni per una migliore leggibilità. Penso che cambino meglio il design e abbiano tutti i parametri con nomi esterni. –

risposta

10

Come altri hanno già detto, è una questione di stile di origine con Objective-C.

Per capire perché qualcuno vorrebbe questo stile, prendere in considerazione una modifica del vostro esempio:

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

che si potrebbe chiamare in questo modo:

say("Hello!") 

Quando si guarda i nomi che si stiamo usando, c'è probabilmente qualche informazione mancante. Con una funzione chiamata say(), potresti ragionevolmente pensare che questa sia una funzione che ti consente di dire qualcosa. Ma quando si guarda il nome del parametro, è abbastanza chiaro che questa è una funzione per dire un saluto, non per dire nulla.

Così Objective-C preferirebbe di scrivere in questo modo:

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

che si potrebbe chiamare in questo modo:

sayGreeting("Hello!") 

ed è ormai chiaro che lei sta dicendo un saluto. In altre parole, il nome della funzione stessa descrive più chiaramente cosa stai facendo. Per questo motivo, lo sayGreeting("Hello!") è preferibile a say(greeting: "Hello!") perché la chiave che una funzione deve essere descritta con il suo nome, non relegata a un nome di parametro e data importanza secondaria.

Ma questo ragionamento funziona solo per il primo argomento. Supponiamo di voler aggiungere un nome, come hai fatto tu. In un linguaggio come C, dove ci sono i nomi dei parametri esterni a tutti, si potrebbe scrivere:

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

e chiamarlo come:

sayGreetingToName("Hello", "Dave"); 

che è OK, ma inizia a dissolversi rapidamente quando si è sovraccaricato funzioni o valori di default, nessuno dei quali si hanno in C. Se si voleva scrivere:

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

poi definendolo come:

sayGreetingToName("Hello", "Dave") 

apparirebbe sostanzialmente OK, ma:

sayGreetingToName("Hello") 

sembra ridicolo, perché il nome della funzione dice che si sta fornendo un nome, ma non lo sei.

Così, invece, se si scrive:

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

si può chiamare in due modi:

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

e tutto sembra perfettamente chiaro.

Quindi, per riassumere, l'idea alla base di questo stile di scrittura è che il nome della funzione stesso dovrebbe contenere qualsiasi informazione contenuta nel nome del primo parametro, ma che non ha senso estenderlo ai parametri successivi. Quindi, per impostazione predefinita, il primo non ha un nome esterno, ma il resto lo fa. La funzione consiste nel dire sempre un saluto, in modo che sia inerente al nome della funzione (e quindi non duplicata insistendo sul nome esterno del primo parametro) ma può o non può riguardare il dirlo a un nome particolare, in modo che le informazioni dovrebbero non essere nel nome della funzione.

consente anche di leggere essenzialmente una chiamata di funzione come se fosse inglese, perché i nomi ed i parametri sono ora circa nel giusto ordine per voi a fare quanto segue:

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

Say (il) saluto, "Ciao", a (la persona con) nome "Dave"

È uno stile piuttosto bello, una volta che ci si abitua.

2

Questo è il comportamento predefinito per le funzioni in rapido, omettendo il nome esterno per il primo parametro di funzione.

Per default, il primo parametro omette suo nome esterno, e le seconda e le successive parametri usare il loro nome locale come il loro nome esterno .

Da Language Guide - Functions.

Nota tuttavia che è possibile aggiungere un nome esterno anche per il primo parametro di funzione, se lo desiderate:

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

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

Allo stesso modo, si può dire al 2 ° (e così via) parametri di funzione di omettere la loro esterna nomi, aggiungendo _ prima del nome interno del parametro, nella firma della funzione.

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

foo2("Hello", "World") 

nota tuttavia che per initializers, i nomi esterni sono obbligatorie per tutti i parametri funzionali, incluso il primo.

Come con parametri di funzionamento e del metodo, i parametri di inizializzazione possono avere sia un nome locale per l'uso all'interno del corpo del inizializzatore e un nome esterno da utilizzare quando si chiama l'inizializzatore.

Tuttavia, gli inizializzatori non hanno un nome di funzione identificativo prima delle parentesi nel modo in cui funzionano le funzioni e i metodi. Pertanto, , i nomi e i tipi dei parametri di un inizializzatore svolgono un ruolo particolarmente importante in nell'identificazione di quale inizializzatore deve essere chiamato. Per questo motivo, Swift fornisce un nome esterno automatico per ogni parametro in un inizializzatore se non si fornisce un nome esterno .

Da Language Guide - Initialization.

A titolo di esempio, si consideri

struct Foo { 
    var bar : Int 

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

var a = Foo(extBar: 1) 

Anche in questo caso, si può dire in modo esplicito il costruttore di lasciare i parametri di omettere i nomi esterni

struct Foo2 { 
    var bar : Int 

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

var a = Foo2(1) 
2

Come @dfri già detto, è appena stato deciso in questo modo.

Nota che si può facilmente personalizzare il comportamento, richiedendo esplicitamente i nomi dei parametri al momento della chiamata e _ omettere loro:

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

risultati nel dover chiamare tramite

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

alternativa

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

say("Goodbye", "Hollywood") 

o

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

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

Chris Lattner parla esattamente questo nel What's New In Swift 2 Talk:

Torna nei giorni di 1.x Swift questo comportamento applicata solo ai metodi. Le funzioni scritte al di fuori delle classi, delle strutture o delle enumerazioni non richiedevano alcun parametro per essere chiamato in una chiamata di funzione (a meno che non lo si forzasse esplicitamente usando un nome di parametro esterno nella definizione della funzione).

A causa delle convenzioni dell'Obiettivo-C, i metodi venivano spesso denominati in un modo, che il primo parametro era già parte del nome del metodo.
Esempi: indexOf(_:) anziché index(of:) o charactersAtIndex(_:) anziché charactersAt(index:).
In Objective-C questo sarebbe scritto come indexOf: e charactersAtIndex:. Non ci sono parentesi graffe per separare il nome della funzione dai parametri di funzione. Quindi i parametri erano fondamentalmente parte del nome della funzione.

Come accennato prima, questo comportamento si applicava solo ai metodi all'inizio però. Ciò ha causato confusione tra i programmatori, quando aggiungere un nome esterno al primo parametro e quando no. Quindi, alla fine, il comportamento è stato modificato, in modo che il primo parametro non utilizzi il nome interno come nome esterno per impostazione predefinita, ma tutti i parametri riportati di seguito.
Ciò ha comportato un utilizzo più coerente dei nomi dei parametri esterni e interni. E questo è il comportamento che esiste oggi in Swift.

tl; dr Il comportamento è un residuo da Objective-C

+1

Solo per essere pedante: questo comportamento non è un residuo * di * objc di per sé, più un residuo * delle convenzioni progettuali API utilizzate con * ObjC. Apple avrebbe potuto anche decidere di dire (salutare: "Ciao", toName: "Mondo") "l'impostazione predefinita e ha comunque avuto il bello dalla storia di Cocoa dei siti di chiamata che leggevano frasi inglesi ... Ma se lo facessero che: a) tutti i metodi ObjC importati sarebbero diversi dai metodi nativi di Swift, o b) avrebbero dovuto fare in modo che l'importatore mappasse in modo impeccabile ogni metodo ObjC come 'sayGreeting: toName:' ​​con un metodo Swift come 'say (saluto: toname:) '. – rickster