Extension du type générique construit dans Swift

Est-il possible d’étendre une classe générique pour un type générique spécialisé / construit? Je voudrais étendre Int Arrays avec une méthode pour calculer la sum de ses éléments.

par exemple

extension Array { func sum() -> Int { return reduce(0) { $0 + $1 } } } 

Cela peut être réalisé en utilisant des extensions de protocole (Voir Le langage de programmation rapide: Protocoles pour plus d’informations). Dans Swift 3:

Pour résumer juste Int s vous pourriez faire:

 extension Sequence where Iterator.Element == Int { var sum: Int { return reduce(0, +) } } 

Usage:

 let nums = [1, 2, 3, 4] print(nums.sum) // Prints: "10" 

Ou, pour quelque chose de plus générique, vous pouvez faire ce que @Wes Campaigne a suggéré et créer un protocole Addable :

 protocol Addable { init() func + (lhs: Self, rhs: Self) -> Self } extension Int : Addable {} extension Double: Addable {} extension Ssortingng: Addable {} ... 

Ensuite, Addable Sequence pour append des séquences d’éléments Addable :

 extension Sequence where Iterator.Element: Addable { var sum: Iterator.Element { return reduce(Iterator.Element(), +) } } 

Usage:

 let doubles = [1.0, 2.0, 3.0, 4.0] print(doubles.sum) // Prints: "10.0" let ssortingngs = ["a", "b", "c"] print(ssortingngs.sum) // Prints: "abc" 

Géré pour faire fonctionner quelque chose de manière extensible et générique sans abuser du système de caractères, mais il présente certaines limites.

 protocol Addable { func +(lhs: Self, rhs: Self) -> Self class var identity: Self { get } } extension Int : Addable { static var identity: Int { get { return 0 } } } extension Ssortingng : Addable { static var identity: Ssortingng { get { return "" } } } extension Array { func sum() -> U? { let s: U? = U.identity return self.sum(s) } func sum(start: U?) -> U? { return reduce(start) { lhs, rhs in switch (lhs, rhs) { case (.Some(let left), let right as U): return left + right default: return nil } } } } 

Plus précisément: avec cette solution, l’inférence de type ne fonctionnera pas avec la méthode sum() sans paramètre, vous devez donc annoter le type de retour attendu ou lui donner une valeur de départ (à partir de laquelle il peut déduire le type).

Notez également que cela retourne une valeur de type facultatif: si pour une raison quelconque, une sum du type attendu ne peut pas être calculée à partir du tableau, elle renvoie nil.

Pour illustrer:

 let int_array = Array(1...10) let x: Int? = int_array.sum() // result: {Some 55} let x2 = int_array.sum(0) // result: {Some 55} let x3 = int_array.sum() // Comstackr error because it can't infer type let ssortingng_array = ["a", "b", "c"] let y: Ssortingng? = ssortingng_array.sum() // result: {Some "abc"} let y2 = ssortingng_array.sum("") // result: {Some "abc"} let y3: Int? = ssortingng_array.sum() // result: nil (can't cast Ssortingng to Int) let y4 = ssortingng_array.sum(0) // result: nil (can't cast Ssortingng to Int) let double_array = [1.3, 4.2, 2.1] let z = double_array.sum(0.0) // Comstackr error because we haven't extended Double to be Addable 

On dirait que tu ne peux pas. Le plus proche que nous pouvons obtenir est la fonction

 func sum(a:Array) -> Int { return a.reduce(0) {$0 + $1} } 

Swift vous permettra d’append une extension à la classe Array mais pas spécifiquement à une version spécialisée de la classe.

error: :108:1: error: non-nominal type 'Array' cannot be extended

Vous pouvez étendre la classe Array.

 extension Array { func sum() -> Int { return reduce(0) { $0 + $1 } } } 

Le problème est maintenant avec l’opérateur +

 error: :102:16: error: could not find an overload for '+' that accepts the supplied arguments return reduce(0) { $0 + $1 } 

Ceci est quelque peu attendu car nous ne pouvons pas être sûr que l’opérateur + sera surchargé pour tous les types possibles qui pourraient être utilisés dans un tableau.

Nous pourrions donc essayer de restreindre l’opération uniquement sur certaines classes. Quelque chose comme

 class Dummy { } extension Array { func someFunc() -> Int { return 0 } } var l = [Dummy()] var r = l.someFunc() // Expect 0 

Conceptuellement, cela devrait fonctionner (actuellement, il semble qu’il y ait un bogue, Xcode se bloque lors de l’évaluation d’un terrain de jeu en utilisant ce code). Au final, ça marche, on ne peut pas utiliser cette astuce puisque le type Int n’est pas une classe.

 extension Array { func sum() -> T { return reduce(0) { $0 + $1 } } } error: :101:14: error: inheritance from non-protocol, non-class type 'Int' func sum() -> T { 

J’ai également envisagé d’étendre la classe Array avec un protocole, mais encore une fois, le fait de ne pas être une classe le rend impossible. Si les types numériques étaient des classes, ce serait bien si nous pouvions avoir un protocole pour définir qu’une classe peut être ajoutée comme Comparable ou Equatable mais je crois que ce protocole ne peut pas définir une fonction générique qui serait nécessaire pour créer un protocole Addable .

Modifier:

Comme indiqué par d’autres réponses, vous pouvez le faire fonctionner pour Int en cochant explicitement Int dans la clôture. Je suppose que j’ai raté ça va enquêter. Mais ce serait toujours bien si nous pouvions avoir une façon générique de travailler avec des types numériques.

Il est possible de retourner une valeur de sum réelle après avoir testé le type int en sum() . Ce faisant, je résoudrais le problème comme suit:

 import Cocoa extension Array { func sum() -> Int { if !(self[0] is Int) { return 0; } var sum = 0; for value in self { sum += value as Int } return sum; } } let array = [1,2,3,4,5] array.sum() // =15 let otherArray = ["SsortingngValue"] otherArray.sum() // =0 

Alexandre,

Voici comment vous pouvez le faire:

 extension Array { func sum() -> Int { return reduce(0) { ($0 as Int) + ($1 as Int) } } } 

Fonctionne comme un charme, testé dans la cour de récréation. Cependant, vous pourriez avoir des problèmes si vous appelez cette fonction sur différents types de baies.

vous pouvez le faire aussi

 extension Array { func sum () -> Int? { guard self.count > 0 && self.first is Int else { return nil } var s = 0 forEach { s += $0 as! Int } return s } }