2014-07-07 11 views
18

Chcę rozszerzyć klasę Array, aby mogła wiedzieć, czy jest sortowana (rosnąco), czy nie. Chcę dodać obliczoną właściwość o nazwie isSorted. Jak mogę określić elementy tablicy jako porównywalne?Rozszerzenie tablicy, aby sprawdzić, czy jest posortowane w Swift?

Moja obecna implementacja w Playground

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 

BłądCould not find an overload for '>' that accepts the supplied arguments

Oczywiście nadal mam błąd, ponieważ Swift nie umie porównać elementy. Jak mogę wdrożyć to rozszerzenie w Swift? Czy robię coś złego tutaj?

+0

możliwy duplikat [Jak wdrożyć spłaszczanie jako rozszerzenie w tablicy bez odlewania?] (Http://stackoverflow.com/questions/24564249/how-to-implement-flatten-as-an-extension-on -an-array-without-type-casting) – Sebastian

+3

Nie można rozszerzyć 'Array ', ale można zaimplementować funkcję działającą na 'Array '. Spójrz na http://stackoverflow.com/a/24565627/1489997 – Sebastian

+0

@Sebastian Myślę, że link, który podałeś jest zupełnie inny niż mój zamiar. Łatwo jest zrobić kategorię dla tego rodzaju rzeczy w obj-c, więc pomyślałem, że powinno być tak samo banalne w Swift. –

Odpowiedz

22

Alternatywne rozwiązanie do wolnej funkcji jest to, co Swift wbudowanej Array.sort i Array.sorted metod zrobić, i wymagają, aby przekazać odpowiednie porównanie metody:

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

Zauważ, że to się nie udaje dla przykładu podanego w pytaniu: '[1, 1, 2, 3, 4, 5, 6, 7, 8] .isSorted (<)', ponieważ nie obsługuje powtarzających się wartości. – nschum

+0

Myślę, że chcesz zmienić 'if! IsOrderedBefore (self [i-1], self [i])' na 'if isOrderedBefore (self [i], self [i-1])', aby naprawić problem wskazany przez nschum. – AmigoNico

+1

Należy zachować ostrożność, to rozwiązanie zawiesiłoby się z pustymi tablicami. Dodałbym strażnika sprawdzającego, czy to nie jest pusta tablica. – juancazalla

7

Wpadłeś na problem z rodzajami Swift, których nie da się rozwiązać tak jak lubisz teraz (może w przyszłej wersji Swift). Zobacz także Swift Generics issue.

Obecnie trzeba zdefiniować funkcję (na przykład w zakresie globalnym):

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 

Komunikat o błędzie jest mylący, IMHO, jako rzeczywisty błąd jest coś takiego jak „nie UIView spełniają ograniczenia typu porównywalne ".

+0

Jestem nadal mylone z takimi przypadkami w szybkim tempie. Nadal trzeba przystosować się z elastyczności wykonawczej obiektu obj-c do ścisłości składni szybkiej (opcje, generics itp.). Sądzę, że moja intencja nie będzie właściwym sposobem myślenia w Swift. –

+0

Oba mają swoje upside i downsides, i oczywiście oba sprawiają, że pewne wzory są łatwiejsze/"bardziej naturalne" niż inne. Zajmie trochę czasu większość z nas, aby nauczyć się korzystać z funkcji Swift w najlepszy możliwy sposób. – DarkDust

20

w Swift 2.0 można teraz rozszerzać protokoły!

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 

Zauważ, że używamy Indexable.Index zamiast prostego Int jako wskaźnik musimy użyć pętli while-zamiast, który wygląda trochę mniej ładna i czysta.

+0

A dla Swift 3, 'Generator' staje się' Iteratorem' i 'someIndex.successor()' staje się 'self.index (po: someIndex)' – markedwardmurray

1

Najbardziej elastyczne rozwiązanie to połączenie odpowiedzi NSAddict i Wesa Campaigne'a. To znaczy. łączą zalety bycia w stanie rozszerzać protokoły i przekazywać funkcje komparatora jako argumenty. Eliminuje to ograniczenia zarówno do korzystania z niego tylko z tablicami, jak i do ograniczania go do elementów zgodnych z protokołem 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 
    } 
} 

ten może być używany na dowolnej Collection typu i kryteria sortowania mogą być definiowane w zależności od potrzeb.

+1

I dla Swift 3 zamień 'someIndex.successor()' na 'self.index (after: someIndex)' – markedwardmurray

Powiązane problemy