2014-11-13 10 views
5

Ho implementato un set in Swift che utilizza le chiavi del dizionario. Voglio implementare un metodo addAll (sequenza) che prende qualsiasi tipo di sequenza sugli Elementi nel Set, ma sto ricevendo un errore che non ha senso. Ecco il mio codiceImplementazione Set.addSequence in Swift

struct Set<Element: Hashable> { 
    var hash = [Element: Bool]() 

    init(elements: [Element] = []) { 
     for element in elements { 
      self.hash[element] = true 
     } 
    } 

    var array: [Element] { 
     return hash.keys.array 
    } 

    func contains(element: Element) -> Bool { 
     return hash[element] ?? false 
    } 

    mutating func add(element: Element) { 
     hash[element] = true 
    } 

    mutating func add(array: [Element]) { 
     for element in array { 
      hash[element] = true 
     } 
    } 

    mutating func add<S : SequenceType where S.Generator.Element == Element>(sequence: S) { 
     for element in sequence { // Error here: "Cannot convert the expression's type 'S' to type 'S' 
      hash[element] = true 
     } 
    } 

    mutating func remove(element: Element) { 
     hash[element] = nil 
    } 
} 

sto ottenendo questo errore in XCode 6.1 e 6.0.1.

Volevo seguire la semantica del metodo di estensione di Array, ma quella firma di tipo non viene nemmeno compilata per me.

Sto facendo qualcosa di sbagliato, o dovrei presentare un radar?

modifica: appena trovato https://github.com/robrix/Set/blob/master/Set/Set.swift, che ha questa implementazione:

public mutating func extend<S : SequenceType where S.Generator.Element == Element>(sequence: S) { 
    // Note that this should just be for each in sequence; this is working around a compiler crasher. 
    for each in [Element](sequence) { 
     insert(each) 
    } 
} 

Tuttavia, che appena converte sequence in un Array, che tipo di sconfigge lo scopo di SequenceType tutto.

risposta

4

Aggiornamento: Questo è stato risolto in Swift 1.2 (Xcode 6.3 beta 3), il codice originale della domanda viene compilato senza errori. (Inoltre, definendo un tipo di set personalizzata non è più necessario, perché Swift 1.2 ha un Set tipo built-in nativo.)


Vecchia risposta: Sembra un bug per me, ma forse qualcuno posso spiegarlo.

possibili soluzioni:

  • Convertire il ragionamento sequence a SequenceOf<Element> esplicitamente:

    mutating func add<S : SequenceType where S.Generator.Element == Element>(sequence: S) { 
        for element in SequenceOf<Element>(sequence) { 
         hash[element] = true 
        } 
    } 
    
  • (Come in https://stackoverflow.com/a/27181111/1187415) Sostituire il ciclo for da un ciclo while utilizzando next() del generatore di sequenze e digitare annotazione l'elemento esplicitamente con element : Element:

    mutating func add<S : SequenceType where S.Generator.Element == Element>(sequence: S) { 
        var gen = sequence.generate() 
        while let element : Element = gen.next() { 
         hash[element] = true 
        } 
    } 
    
  • (da "Creating a Set Type in Swift") Utilizzare map:

    mutating func add<S : SequenceType where S.Generator.Element == Element>(sequence: S) { 
        map(sequence) { 
         self.hash[$0] = true 
        } 
    } 
    
+0

Sono d'accordo che questo è un bug. Qualcuno lo ha segnalato? Grazie – drasto

+1

@drasto: non lo so. Ma in ogni caso, se sei affetto da questo problema, invia il tuo bug report. –

+0

L'uso di 'map' sembra la soluzione migliore. Suppongo che il problema principale sia che 'map' non dovrebbe permettere effetti collaterali. – bart

3

il meglio che potevo venire con era la soluzione mappa che Martin ha anche prodotto.È interessante notare che, espandendo manualmente il ciclo for come:

mutating func add<S : SequenceType where S.Generator.Element == Element>(sequence: S) { 
    var generator = sequence.generate() 
    while let item = generator.next() { 
     self.hash[item] = true 
    } 
} 

Produce l'ennesimo messaggio di errore generator.next():

Cannot convert the expression's type '()' to type 'Self.Element??' 

Potrebbe essere un po 'più ottimale da utilizzare al posto di ridurre la carta, come quello non lo fa costruire un array da scartare:

mutating func add<S : SequenceType where S.Generator.Element == Element>(sequence: S) { 
    reduce(sequence,()) { 
     self.hash[$1] = true 
    } 
} 
+0

La tua prima variante può essere resa funzionante con 'while let item: Element = generator.next() {..}'. Buona idea con 'reduce()'! (Ma entrambi i map/riducono sono solo hack, un semplice 'for in' dovrebbe funzionare.) –

+0

Questo messaggio di errore è una finestra nel bug -' for ... in' chiama davvero 'generate()', quindi il errore è probabile che cosa stia bloccando anche il 'for ... in'. –

+0

@MartinR ha accosentito, stava lanciando più informazioni e opzioni là fuori. Sicuramente qualcosa che l'OP dovrebbe pubblicare su bugs.apple.com –