2014-07-07 31 views
18

Desidero estendere la classe Array in modo che possa sapere se è ordinata (crescente) o meno. Voglio aggiungere una proprietà calcolata denominata isSorted. Come posso affermare che gli elementi della matrice sono comparabili?Estensione array per verificare se è ordinato in Swift?

mio attuale implementazione in campo da giuoco

extension Array { 
    var isSorted: Bool { 
    for i in 1..self.count { 
     if self[i-1] > self[i] { return false } 
    } 
    return true 
    } 
} 

// The way I want to get the computed property 
[1, 1, 2, 3, 4, 5, 6, 7, 8].isSorted //= true 
[2, 1, 3, 8, 5, 6, 7, 4, 8].isSorted //= false 

L'erroreCould not find an overload for '>' that accepts the supplied arguments

Certo, ho ancora ottenuto un errore perché Swift non sa come confrontare gli elementi. Come posso implementare questa estensione in Swift? O sto facendo qualcosa di sbagliato qui?

+0

possibile duplicato di [Come implementare flatten come estensione su un array senza type cast?] (Http://stackoverflow.com/questions/24564249/how-to-implement-flatten-as-an-extension-on -an-array-senza-type-casting) – Sebastian

+3

Non è possibile estendere 'Array ', ma è possibile implementare una funzione che funziona su 'Array '. Dai un'occhiata a http://stackoverflow.com/a/24565627/1489997 – Sebastian

+0

@Sebastian Penso che il collegamento che hai dato sia molto diverso dalla mia intenzione. È abbastanza facile creare una categoria per questo tipo di cose in obj-c, quindi ho pensato che fosse così banale in Swift. –

risposta

22

La soluzione alternativa ad una funzione libera è di fare ciò che Swift di built-in Array.sort e Array.sorted metodi fanno, e richiedono che si passa un comparatore adatto al metodo:

extension Array { 
    func isSorted(isOrderedBefore: (T, T) -> Bool) -> Bool { 
     for i in 1..<self.count { 
      if !isOrderedBefore(self[i-1], self[i]) { 
       return false 
      } 
     } 
     return true 
    } 
} 

[1, 5, 3].isSorted(<) // false 
[1, 5, 10].isSorted(<) // true 
[3.5, 2.1, -5.4].isSorted(>) // true 
+0

Si noti che questo in realtà non riesce per l'esempio fornito nella domanda: '[1, 1, 2, 3, 4, 5, 6, 7, 8] .isSorted (<)', perché non gestisce i valori ripetuti. – nschum

+0

Penso che vogliate cambiare 'if! IsOrderedBefore (self [i-1], self [i])' a 'if isOrderedBefore (self [i], self [i-1])' per risolvere il problema evidenziato da nschum. – AmigoNico

+1

Attenzione, questa soluzione si bloccherebbe con gli array vuoti. Vorrei aggiungere una guardia che controlla che non sia una matrice vuota – juancazalla

7

Hai riscontrato un problema con i generici di Swift che non possono essere risolti nel modo in cui ti piace adesso (forse in una futura versione di Swift). Vedi anche Swift Generics issue.

Attualmente, è necessario definire una funzione (ad esempio presso la portata globale):

func isSorted<T: Comparable>(array: Array<T>) -> Bool { 
    for i in 1..<array.count { 
     if array[i-1] > array[i] { 
      return false 
     } 
    } 

    return true 
} 

let i = [1, 2, 3] 
let j = [2, 1, 3] 
let k = [UIView(), UIView()] 
println(isSorted(i)) // Prints "true" 
println(isSorted(j)) // Prints "false" 
println(isSorted(k)) // Error: Missing argument for parameter #2 in call 

Il messaggio di errore è fuorviante, IMHO, come l'errore effettivo è qualcosa di simile "UIView non lo fa soddisfare il vincolo di tipo Comparabile ".

+0

I'm ancora confuso con questo tipo di casi in modo rapido. Ancora bisogno di adattarsi dalla flessibilità di runtime di obj-c alla severità della sintassi di swift (opzionali, generici, ecc.). Immagino che la mia intenzione non sia il modo giusto di pensare in Swift dopotutto. –

+0

Entrambi hanno i loro lati positivi e negativi e, naturalmente, entrambi rendono alcuni modelli più semplici/"più naturali" rispetto agli altri. Ci vorrà del tempo per la maggior parte di noi per imparare come utilizzare le funzionalità di Swift nel miglior modo possibile. – DarkDust

20

In Swift 2.0 è ora possibile estendere i protocolli!

extension CollectionType where Generator.Element: Comparable { 

    public var isSorted: Bool { 

     var previousIndex = startIndex 
     var currentIndex = startIndex.successor() 

     while currentIndex != endIndex { 

      if self[previousIndex] > self[currentIndex] { 
       return false 
      } 

      previousIndex = currentIndex 
      currentIndex = currentIndex.successor() 
     } 

     return true 
    } 

} 

[1, 2, 3, 4].isSorted // true 
["a", "b", "c", "e"].isSorted // true 
["b", "a", "c", "e"].isSorted // false 
[/* Anything not implementing `Comparable` */].isSorted // <~~ Type-error 

Nota che perché stiamo usando Indexable.Index al posto di un semplice Int come un indice dobbiamo usare un ciclo while, invece, che sembra un po 'meno bella e chiara.

+0

E per Swift 3, 'Generator' diventa' Iterator' e 'someIndex.successor()' diventa 'self.index (after: someIndex)' – markedwardmurray

1

La soluzione più flessibile per me è una combinazione della risposta di NSAddict e Wes Campaigne. Cioè unire il vantaggio di poter estendere i protocolli e passare le funzioni di confronto come argomenti. Ciò elimina le restrizioni sia per utilizzarlo solo con gli array e per vincolarlo agli elementi conformi al protocollo Comparable.

extension CollectionType 
{ 
    func isSorted(isOrderedBefore: (Generator.Element, Generator.Element) -> Bool) -> Bool 
    { 
     var previousIndex = startIndex 
     var currentIndex = startIndex.successor() 

     while currentIndex != endIndex 
     { 
      if isOrderedBefore(self[previousIndex], self[currentIndex]) == false 
      { 
       return false 
      } 

      previousIndex = currentIndex 
      currentIndex = currentIndex.successor() 
     } 

     return true 
    } 
} 

questo può essere utilizzato su qualsiasi tipo Collection e criteri di ordinamento possono essere definiti in base alle proprie esigenze.

+1

E per Swift 3 sostituisci 'someIndex.successor()' con 'self.index (after: someIndex)' – markedwardmurray

Problemi correlati