2017-10-10 23 views
0

Creuser dans la programmation fonctionnelle et rapide dans l'ensemble, je suis submergé par de multiples façons de faire les choses. Dans ce cas, j'aimerais avoir struct qui adopte Comparable mais qui peut conditionnellement changer quelles propriétés sont utilisées dans les opérateurs surchargés.Comment basculer de manière conditionnelle entre les implémentations de méthodes d'extension de protocole?

Disons que je suit, un tri rapide (du didacticiel Wenderlich FP Niv Yahel), étendant une gamme comparable, qui facilement accueillir mon Collection de Student s

struct Student { 
    let name: String 
    let age: Int 
    let grades: Double 
} 

extension Student: Comparable { 
    static func <(lhs: Student, rhs: Student) -> Bool { 
     return lhs.grades < rhs.grades 
    } 
    static func ==(lhs: Student, rhs: Student) -> Bool { 
     return lhs.grades == rhs.grades 
    } 
} 

extension Array where Element: Comparable { 
    func quickSorted() -> [Element] { 
     if self.count > 1 { 
      let (pivot, remaining) = (self[0], dropFirst()) 
      let lhs = remaining.filter{ $0 <= pivot } 
      let rhs = remaining.filter{ $0 > pivot } 
      return lhs.quickSorted() as [Element] + pivot + rhs.quickSorted() 
      } 
     return self 
     } 
    } 
} 

//Omitted, create a bunch of Students 
//let bingoLittle = Student(name: "BingoLittle", age: 23, grades: 93.4) 
let myStudentDirectory = [bingoLittle, studentB, ... StudentN] 
let sortedStudentDirectory = myStudentDirectory.quickSorted() 

Mais, ce que je voudrais que une prochaine étape est de décider à la volée où la structure sera triée par, soit le nom, les grades, ou l'âge, de préférence sans avoir à toucher cette belle fonction de quicksort.

  1. Faut-il transformer le tri rapide en une fonction générique?
  2. Devrais-je examiner les contraintes de type?
  3. Devrais-je avoir une propriété dans Student qui est une énumération de la propriété sur laquelle elle devrait trier? Semble moche.
  4. Dois-je avoir un quickSorted qui est quelque chose comme quickSorted(by: .name)? Il ne semblera plus bien s'appliquer à une extension de tableau.

Répondre

0

Il existe plusieurs façons d'aborder ceci:

1) utiliser les fonctions de tri natives qui vous permettent de spécifier une fermeture pour la comparaison et offre ainsi une plus grande flexibilité et ne nécessite pas que votre struct soit Comparable :

let sortedStudentDirectory = myStudentDirectory.sorted{ $0.grade < $1.grade } 

// 
// This would be my recommendation given that it is standard and 
// it is unlikely that the quicksorted() method would outperform it. 
// 

2) modifier la fonction quicksorted() pour le faire fonctionner avec une fermeture:

extension Array 
{ 
    func quickSorted<T:Comparable>(_ property:@escaping (Element)->T) -> [Element] 
    { 
     guard self.count > 1 else { return self } 
     let (pivot, remaining) = (property(self[0]), dropFirst()) 
     let lhs = remaining.filter{ property($0) <= pivot } 
     let rhs = remaining.filter{ property($0) > pivot } 
     return lhs.quickSorted(property) as [Element] + self[0] + rhs.quickSorted(property) 
    } 
} 

let sortedStudentDirectory = myStudentDirectory.quickSorted{$0.grades} 

// this one also avoids making the struct Comparable. 
// you could implement it like the standard sort with a comparison 
// closure instead of merely a property accessor so that descending 
// sort order and multi-field sorting can be supported. 

.

3) Ajouter une variable statique à votre struct étudiant de dire à votre opérateur de comparaison champ à utiliser et à cette variable statique avant d'utiliser la fonction quicksorted()

struct Student 
{ 
    enum SortOrder { case name, age, grades } 
    static var sortOrder = .grades 
    let name: String 
    let age: Int 
    let grades: Double 
} 

extension Student: Comparable 
{ 
    static func <(lhs: Student, rhs: Student) -> Bool 
    { 
     switch Student.sortOrder 
     { 
      case .grades : return lhs.grades < rhs.grades 
      case .age : return lhs.age < rhs.age 
      default  : return lhs.name < rhs.name 
     } 
    } 

    static func ==(lhs: Student, rhs: Student) -> Bool 
    { 
     switch Student.sortOrder 
     { 
      case .grades : return lhs.grades == rhs.grades 
      case .age : return lhs.age == rhs.age 
      default  : return lhs.name == rhs.name 
     } 
    } 
} 

Student.sortOrder = .grades 
let sortedStudentDirectory = myStudentDirectory.quickSorted() 

Ce dernier est assez mauvaise et l'erreur Prone car il aura un impact sur d'autres opérations de comparaison sur la structure qui ne peut pas avoir l'intention de le trier (en particulier pour l'opérateur ==). Ce n'est pas non plus thread thread.