2015-04-18 35 views
6

Mentre find(["a", "b"], "c") funziona senza problemi, viene visualizzato un errore durante il tentativo di trovare l'indice di una struttura all'interno una matrice di strutture:Swift Impossibile richiamare 'find' con un elenco di argomenti di tipo '([Partitura], Punteggio) in cui Score è una struct

struct Score 
{ 
    //... 
} 

var scores: [Score] = //... 
var score: Score = //... 

find(self.scores, score) // Error: Cannot invoke 'find' with an argument list of type '([Score], Score)' 

ho pensato che potrebbe essere un problema con le strutture che non possono essere comparati a vicenda per default. Ma cambiare la definizione di Score a class mi dà lo stesso errore.

+0

Hai già trovato il motivo. 'Score' deve implementare il protocollo' Equatable' (non importa se struct o class). –

+1

Probabilmente lo stesso problema qui: http://stackoverflow.com/questions/27887387/how-to-use-contains-with-two-arrays-of-objects –

risposta

14

MODIFICA: da Swift 2.0, ora c'è una versione integrata di find che richiede una chiusura in modo da non doverne scrivere la propria, ma anche, find è stata rinominata indexOf e ora è un'estensione di protocollo di CollectionType, quindi la si chiama come un metodo:

// if you make `Score` conform to `Equatable: 
if let idx = self.scores.indexOf(score) { 

} 

// or if you don't make it Equatable, you can just use a closure: 
// (see original answer below for why you might prefer to do this) 
if let idx = scores.indexOf({$0.scoreval == 3}) { 

} 

originale risposta pre-2.0 di seguito


Mentre le risposte suggerendo rendendo la classe Equatable può funzionare bene, mi consiglia un po 'di cautela prima di scegliere di fare questo. Il motivo è che, come dichiarano i documenti, l'equatablabilità implica la sostituibilità e l'operatore == deve essere riflessivo, simmetrico e transitivo. Se non si aderisce a questo, si potrebbe ottenere un comportamento molto strano quando si utilizzano algoritmi come equals, sort ecc. Prestare particolare attenzione se si implementa Equatable in classi non finali. Se sei sicuro di poter soddisfare i requisiti, prova a farlo e lo standard find funzionerà.

In caso contrario, in alternativa si potrebbe prendere in considerazione è la scrittura di una funzione che dovrebbe di essere nella libreria standard, ma non è, che è un find che prende una chiusura:

func find<C: CollectionType>(source: C, match: C.Generator.Element -> Bool) -> C.Index { 
    for idx in indices(source) { 
     if match(source[idx]) { return idx } 
    } 
    return nil 
} 

Una volta che avete questo, puoi fornire qualsiasi criterio di corrispondenza che preferisci. Ad esempio, se i tuoi oggetti sono classi potresti usare l'uguaglianza di riferimento:

let idx = find(scores) { $0 === $1 } 
+0

Sembra che 'indexOf' sia stato rinominato in' index (of:) 'in Swift 3. – Pang

7

L'interfaccia per la funzione find è/era:

func find<C : CollectionType where C.Generator.Element : Equatable>(domain: C, 
    value: C.Generator.Element) -> C.Index? 

Questo dice che il CollectionType di C deve avere elementi che sono Equatable e, inoltre, che il value deve anche essere Equatable.

[Nota Swift 3.0: A partire da Swift 3.0, è necessario utilizzare la funzione index disponibile in due varianti. Nella prima, si fornire il proprio predicato:

func index(where: (Self.Generator.Element) -> Bool) -> Self.Index? 

Nel secondo, gli elementi devono essere equatable:

// Where Generator.Element : Equatable 
func index(of: Self.Generator.Element) -> Self.Index? 

Se si decide di andare via equatable, si applica il seguente . Nota Fine]

tuo Score struct non è Equatable e quindi l'errore. Dovrai capire cosa significa che i punteggi siano uguali tra loro. Forse è un "punteggio" numerico; forse è un 'punteggio' e un 'id utente'. Dipende dall'astrazione Score. Una volta che sai, è implementare == utilizzando:

func == (lhs:Score, rhs:Score) -> Bool { 
return // condition for what it means to be equal 
} 

Nota: se si utilizza class e quindi i punteggi hanno 'identità', allora è possibile implementare questa come:

func == (lhs:Score, rhs:Score) -> Bool { return lhs === rhs } 

Il vostro esempio con le stringhe funziona perché String è Equatable. Se si guarda nel codice della libreria Swift si vedrà:

extension String : Equatable {} 
func ==(lhs: String, rhs: String) -> Bool 
1

Come gli altri hanno detto, gli oggetti si cerca deve essere conforme al protocollo equatable.

Quindi è necessario aggiungere un'estensione al vostro struct punteggio che dice al compilatore che è conforme a quel protocollo:

extension Score: Equatable {} 

E poi è necessario implementare la funzione == per la classe:

public func ==(lhs: Score, rhs: Score) -> Bool 
{ 
    return lhs.whatever == rhs.whatever //replace with code for your struct. 
} 
+0

Perché ottengo lo stesso errore quando si esegue 'Score' un 'class' invece? Non è '==' valido per le classi? – Rivera

+0

No. == non è valido per nessun oggetto personalizzato a meno che non lo definisca come descritto nella mia risposta. –