Le subscripting en Swift

Dans ce cours nous allons regarder comment fonctionne le subscripting ou indice (généralement appelé subscripts) en Swift. Tout d’abord le subscripting est un sucre syntaxique qui fournit un raccourcis pour accéder (ou écrire) à des éléments. Par example lorsque vous utilisez un tableau ou une collection vous utilisez généralement cette notation array[index] ou collection[key] qui ne sont autre que des subscripts (notation []).

Syntaxe de déclaration d’un subscript

Les subscripts respectent le même format que les propriétés calculées (avec les get et set définis manuellement) à ceci prêt que ce ne sont pas des variables. On utilise le mot-clé subscript pour définir un indice :

subscript(index: Int) -> Int {
  get {
    // on retourne la valeur ici
  }
  set(newValue) {
    // on écrit la valeur ici
  }
}

La syntaxe ressemble à un mélange entre une fonction et une propriété calculée. La signature de l’indice permet de définir le ou les paramètres d’entrées et le type de retour attendu. On peut utiliser n’importe quel type en entrée, même les paramètres variadiques (avec les ...), par contre les paramètres en inout et avec des valeurs par défaut sont à proscrire. Ensuite on défini le getter et le setter. On peut aussi utiliser le subscript en lecture seule en éliminant le setter (set).

Voici un example d’implémentation de subscript en lecture seule qui définit une structure pour représentant la table de multiplication pour le nombre 4 :

struct TableDe4 {
  private let multiplier = 4

  subscript(index: Int) -> Int {
    return multiplier * index
  }
}

let tablede4 = TableDe4()

print("six fois 4 fait \(tablede4[6])")
// Affiche : "six fois 4 fait 24"

Dans cet example, vous pouvez interroger l’instance tablede4 en appelant sont subscript comme montré avec l’appel de tablede4[6]. Cette déclaration demande la sixième entrée dans la tablede4 qui retourne la valeur 24 (résultat de 4 * 6).

Example de Subscript

Ici nous allons travailler avec un example plus concret qui utilise plusieurs indices à savoir une matrice :

struct Matrice {
  let lignes: Int, colonnes: Int

  var valeurs: [Double]

  init(lignes: Int, colonnes: Int) {
    self.lignes   = lignes
    self.colonnes = colonnes

    valeurs = Array(count: lignes * colonnes, repeatedValue: 0.0)
  }

  subscript(ligne: Int, colonne: Int) -> Double {
    get {
      return valeurs[(ligne * colonne) + colonne]
    }
    set {
      valeurs[(ligne * colonne) + colonne] = newValue
    }
  }
}

Dans cet example nous créons une structure appelé Matrice qui fournit un initialiser qui prend en 2 paramètres d’entrées appelés lignes et colonnes et qui créer un tableau qui est assez grand pour contenir lignes * colonnes valeurs de type Double. Chaque valeur dans la matrice est initialisé à 0.0 grâce à la méthode Array(count: repeatedValue:).

Vous pouvez construire une nouvelle Matrice en passant le nombre approprié de ligne et de colonne lors de l’initialisation :

var matrice = Matrice(lignes: 4, colonnes: 4)

Les valeurs dans la matrice peuvent être écrite en passant le numéro de ligne et de colonne dans le subscript séparé par une virgule :

matrice[1, 2] = 14.2
matrice[2, 3] = 8.9
matrice[3, 3] = 3.6

Et de la même manière que précédemment on peut récupérer les valeurs en utilisant les subscripts :

print("\(matrice[1, 2])") // Affiche : 14.2 
print("\(matrice[2, 3])") // Affiche : 8.9 
print("\(matrice[3, 3])") // Affiche : 3.6 

Bien évidement dans cet example on ne teste pas les limites aux bornes, mais il est très important de ne pas utiliser des valeurs en dehors des limites du tableau de valeur.

Paramètres variadiques

Afin d’illustrer rapidement l’utilisation des paramètres variadiques nous allons ajouter un subscript à notre structure :

subscript(positions: (l: Int, c: Int)...) -> [Double] {
  get {
    var result: [Double] = []

    for pos in positions {
      result.append(self[pos.l, pos.c])
    }

    return result
  }
  set {
    for (index, pos) in positions.enumerate() {
      self[pos.l, pos.c] = newValue[index]
    }
  }
}

Ici le but de ce subscript est de pouvoir écrire et récupérer plusieurs valeurs en un seul appel. Voici par example comment écrire plusieurs valeurs d’un seul coup :

matrice[(l: 1, c: 2), (l: 2, c: 3), (l: 3, c: 3)] = [14.2, 8.9, 3.6]

Et pour lire plusieurs valeurs, rien de plus simple :

print(matrice[(l: 1, c: 2), (l: 2, c: 3), (l: 3, c: 3)])

// Affiche : [14.2, 8.9, 3.6]

Conclusion

Les subscripts étant rien de plus que des propriétés calculées, ils ne rajoute pas de complexité à l’utilisation de Swift. Vous pouvez l’utiliser pour appeler d’autres fonctions, pour dessiner à l’écran, ou tout autre usage farfelu. Lorsque vous utilisez un subscript veillez à ce que vous les traitiez comme des indices ou des index de la même manière qu’un tableau ou un dictionnaire et donc en faisant attention aux bornes limites.

Le subscripting est une syntaxe puissante du langage Swift et nous devons nous efforcer à l’utiliser à bon escient pour éviter de rajouter du désordre à notre code.

J’espère que vous avez trouvé cet article utile. Si c’est le cas, n’hésiter à le partager sur les réseaux sociaux ou ailleurs. Le blog est encore assez nouveau, et chaque partage compte. Bien sûr, si vous avez des questions, ne pas hésiter à me laisser des commentaires ou nous contacter sur @swittuto. Merci !

1 Etoile2 Etoiles3 Etoiles4 Etoiles5 Etoiles (2 votes, average: 3,00 out of 5)
Loading...

2 commentaires

Time limit is exhausted. Please reload CAPTCHA.

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

  1. Lebreton · 24 février 2016

    Félicitations pour la régularité des nouveaux articles

    • yannickl · 24 février 2016

      Merci, on va essayer de continuer comme ça. Même si le mois de mars risque d’être un peu plus léger sur la fréquence des publications.