2015-01-06 23 views
6

Sto cercando di creare alcune definizioni di chiusura che userò molto nella mia app iOS. Così ho pensato di utilizzare un typealias come sembrava il più promettente ...Swift: riutilizzo di una definizione di chiusura (con typealias)

ho fatto un piccolo esempio giochi che mostra il mio problema in dettaglio

// Here are two tries for the Closure I need 
typealias AnonymousCheck = (Int) -> Bool 
typealias NamedCheck = (number: Int) -> Bool 

// This works fine 
var var1: AnonymousCheck = { 
    return $0 > 0 
} 
var1(-2) 
var1(3343) 

// This works fine 
var var2: NamedCheck = { 
    return $0 > 0 
} 
var2(number: -2) 
var2(number: 12) 

// But I want to use the typealias mainly as function parameter! 
// So: 

// Use typealias as function parameter 
func NamedFunction(closure: NamedCheck) { 
    closure(number: 3) 
} 
func AnonymousFunction(closure: AnonymousCheck) { 
    closure(3) 
} 

// This works as well 
// But why write again the typealias declaration? 
AnonymousFunction({(another: Int) -> Bool in return another < 0}) 
NamedFunction({(another: Int) -> Bool in return another < 0}) 

// This is what I want... which doesn't work 
// ERROR: Use of unresolved identifier 'number' 
NamedFunction({NamedCheck in return number < 0}) 

// Not even these work 
// ERROR for both: Anonymous closure arguments cannot be used inside a closure that has exlicit arguments 
NamedFunction({NamedCheck in return $0 < 0}) 
AnonymousFunction({AnonymousCheck in return $0 < 0}) 

Mi manca qualcosa o non è solo supportato in Swift? Grazie

EDIT/aggiunta:

Quanto sopra è solo un esempio semplice. Nella vita reale le mie tipicità sono più complicate. Qualcosa di simile:

typealias RealLifeClosure = (number: Int, factor: NSDecimalNumber!, key: String, upperCase: Bool) -> NSAttributedString 

Io fondamentalmente voglio usare un typealias come una scorciatoia in modo da non dover digitare più di tanto. Forse le tipealie non sono la scelta giusta ... ce n'è un'altra?

risposta

9

Non stanno riscrivendo la dichiarazione typealias in questo codice, si sta dichiarando i parametri e il tipo di ritorno:

AnonymousFunction({(another: Int) -> Bool in return another < 0}) 

Fortunatamente, inferenza di tipo di Swift consente di utilizzare qualsiasi delle seguenti - scegliere lo stile che ti senti meglio:

AnonymousFunction({ (number: Int) -> Bool in number < 0 }) 
AnonymousFunction { (number: Int) -> Bool in number < 0 } 
AnonymousFunction { (number) -> Bool in number < 0 } 
AnonymousFunction { number -> Bool in number < 0 } 
AnonymousFunction { number in number < 0 } 
AnonymousFunction { $0 < 0 } 
+0

Sì OK, sto dichiarando i parametri e il tipo di reso. Ma sarebbe bello se potessi usare il tipo di alias che ho già definito per farlo. Ecco cosa sono fondamentalmente dopo ... – Soko

+2

Pensa al tuo tipo di alias come un * tipo * - non può diventare i parametri effettivi che sono mirati a quella chiusura, definisce solo quello che sono. È la differenza tra 'number' e' Int' in 'let numero: Int = 3'. –

+0

OK ... ho capito cosa intendi. Dai un'occhiata però alla mia modifica della domanda. Pensavo solo di poter usare le tipografie per risparmiarmi a digitare continuamente chiusure complesse. Ma sembra che non posso farlo. – Soko

2

La sintassi per creare una chiusura è:

{ (parameters) -> return type in 
    statements 
} 

Qual è a sinistra della in è la firma di chiusura (parametri e valore di ritorno). In alcuni casi la firma può essere omessa o semplificata quando l'inferenza di tipo è in grado di determinare il numero di parametri e il loro tipo e il valore di ritorno.

Nel tuo caso non funziona perché stai passando un alias di tipo, ma è interpretato come un nome di parametro. Le 3 linee funzionano se uno si:

  1. Name Il parametro correttamente

    NamedFunction({number in return number < 0}) 
    AnonymousFunction({number in return number < 0}) 
    
  2. usano argomenti stenografia:

    NamedFunction({ return $0 < 0}) 
    AnonymousFunction({ return $0 < 0}) 
    
  3. argomenti uso stenografia e rendimento implicito:

    NamedFunction({ $0 < 0}) 
    AnonymousFunction({ $0 < 0}) 
    
+0

Grazie Antonio. Quello che mi piace ottenere con le tipografie è abbreviare la mia digitazione. La chiusura della vita reale è molto più complessa che nell'esempio. Vedi la mia modifica/aggiunta della domanda originale ... – Soko

6

Non penso che sarai in grado di fare quello che vuoi.Per semplificare un po 'il tuo esempio, si può fare questo:

typealias NamedCheck = (number: Int) -> Bool 
let f: NamedCheck = { $0 < 5 } 
f(number: 1) 
NamedFunction(f) 

NamedFunction({ $0 < 5 } as NamedCheck) 

Ma non si può fare quello che vuoi, che è quello di fare affidamento sul fatto che l'arg tupla è chiamata number fare riferimento ad esso all'interno della chiusura, senza dando come parte della chiusura:

// compiler error, no idea what "number" is 
let g: NamedCheck = { number < 5 } 

Tenete a mente che è possibile denominare il parametro senza dargli un tipo (che viene dedotto dal tipo di g):

let g: NamedCheck = { number in number < 5 } 

, ma anche, si può chiamarla come volete:

let h: NamedCheck = { whatevs in whatevs < 5 } 
NamedFunction(h) 

Ecco cosa penso che sta accadendo (questo è in parte congetture). Ricordate come funzioni possono avere nomi degli argomenti esterni e interni:

func takesNamedArgument(#namedArg: Int) { etc... } 

Oppure, per scriverla longhand:

func takesNamedArgument(namedArg namedArg: Int) { etc... } 

Ma si può anche dare come secondo, interno, nome quello che vuoi:

func takesNamedArgument(namedArg whatevs: Int) { etc... } 

Penso che questo sia quello che sta succedendo con le chiusure con tuple denominate. Il nome "esterno" è "numero", ma devi dargli anche un nome "interno", che è ciò che devi usare nel corpo della funzione. Non puoi usare l'argomento esterno nella tua funzione. In caso di espressioni di chiusura, se non si assegna un nome interno, è possibile utilizzare $0 ecc., Ma non si può semplicemente ignorarlo, non più di quanto sia possibile saltare del tutto il nome interno e basarsi solo sul nome esterno quando si definisce una funzione regolare.

speravo che avrei potuto dimostrare questa teoria dal seguente:

let f = { (#a: Int, #b: Int)->Bool in a < b } 

conseguente f essere di tipo (a: Int, b: Int)->Bool). Questo compila, come fa:

let g = { (number1 a: Int, number2 b: Int)->Bool in a < b } 

ma non sembra che i nomi esterni per l'argomento fanno fuori per il tipo di f o g.