Tutoriel Swift: Introduction aux génériques

Advertisement

Tutoriel Swift: Introduction aux génériques

Pourquoi faire trois boîtes quand un générique va faire?

Programmation générique est une façon d'écrire des fonctions et types de données sans être précis sur les types qu'ils utilisent. Les tableaux sont un bon exemple - si vous avez un tableau de chaînes ou un tableau d'entiers ou un tableau de AnyObject , ils sont tous les tableaux qui fonctionnent de la même manière.

La différence est le genre de chose qu'ils stockent, mais il y a encore un seul Array de type. La grande idée ici est que vous dites ce que le tableau tiendra lorsque vous le déclarer. D'une certaine manière, c'est comme si un type devenait un paramètre supplémentaire d'une fonction, vous évitant d'écrire de nombreuses versions d'une fonction ou d'un type de données similaire.

Vous trouverez des génériques en usage dans Swift, ce qui rend leur compréhension essentielle à une compréhension complète de la langue. Dans ce didacticiel, vous expérimenterez dans un terrain de jeu Swift pour apprendre ce que sont les génériques et pourquoi ils sont utiles, comment écrire des fonctions génériques et des structures de données, ainsi que des tâches plus intermédiaires, comme la façon d'utiliser les contraintes de type et étendre les types génériques . Enfin, vous allez jeter un oeil à certaines des nouvelles fonctionnalités génériques dans Swift 2.

Remarque: Ce tutoriel nécessite Xcode 7 et Swift 2.

Commencer

Commencez par créer un nouveau terrain de jeu: Dans Xcode, allez dans Fichier \ New \ Playground ..., nommez les Generics de jeux et sélectionnez OS X comme la plate - forme. Cliquez sur Suivant pour choisir l' endroit où vous souhaitez enregistrer votre nouveau terrain de jeu et enfin, cliquez sur Créer.

Supposons que votre application nécessite une fonction pour ajouter deux entiers. Ajoutez ce qui suit à votre aire de jeux nouvellement créée:

  Func adderInt (x: Int, _ y: Int) -> Int {return x + y} 

adderInt(_:_:) prend deux Int valeurs et renvoie leur somme. Vous pouvez essayer en ajoutant le code suivant à l'aire de jeux:

  Let intSum = adderInt (1, 2) 

C'est un exemple assez simple qui montre également la sécurité de type Swift. Vous pouvez appeler cette fonction avec deux entiers, mais pas n'importe quel autre type. Maintenant , supposons que vous avez aussi besoin d'ajouter deux Double valeurs. Vous souhaitez créer une seconde fonction adderDouble :

  (X: Double, _ y: Double) -> Double {return x + y} let doubleSum = adderDouble (1.0, 2.0) 

La fonction signatures de adderInt et adderDouble sont différents, mais les corps des fonctions sont identiques. Non seulement vous avez deux fonctions, vous avez également répété le code à l'intérieur d'eux. Bientôt, vous verrez comment vous pouvez utiliser génériques pour réduire ces deux fonctions à un et supprimer le code redondant.

Matrices en tant que types génériques

Ajoutez ce qui suit à votre aire de jeux:

  Let nombres = [1, 2, 3] let firstNumber = numbers [0] 

Ici, vous créez un tableau simple de trois nombres et puis prenez le premier nombre hors de ce tableau.

Maintenant Option-cliquez d' abord sur le numbers et sur firstNumber . Que vois-tu? Parce que Swift a l' inférence de type, vous ne devez pas définir explicitement les types de vos constantes, mais ils ont tous deux un type exact: numbers est un [Int] c'est-un tableau d'entiers et firstNumber est un Int .

Le Swift Array type est un type générique, ou qui nécessite un paramètre de type afin d'être entièrement spécifié. Via l' inférence de type et aussi grâce à la sécurité de type Swift, les numbers ne peuvent contenir que Int valeurs, et quand vous prenez quelque chose hors de ce tableau, Swift et plus important encore, vous , à la fois le savoir doit être un Int .

Vous pouvez mieux voir la nature générique de Array en renonçant à l' inférence de type et le sucre syntaxique de tableau Swift et en ajoutant le code suivant à la fin de l'aire de jeu:

  Var numbersAgain = Matrice <Int> () numbersAgain.append (1) numbersAgain.append (2) numbersAgain.append (3) let firstNumberAgain = numbersAgain [0] 

Vérifiez les types de numbersAgain et firstNumberAgain par Option-cliquant dessus; Les types seront exactement les mêmes que les valeurs précédentes. Ici , vous spécifiez le type de numbersAgain en utilisant la syntaxe générique explicite, en mettant Int entre crochets après Array .

Essayez annexant autre chose à l'ensemble, comme un Double ou d' une String :

  NumbersAgain.append ("hello") 

Vous obtiendrez une erreur quelque chose comme, Cannot invoke 'append' with an argument list of type '(String)' . Le compilateur vous indique que vous ne pouvez pas ajouter une chaîne à un tableau d'entiers. Comme une méthode sur le type générique Array , append est une méthode dite générique. Il connaît le type d'éléments du tableau contenant, et ne vous laissera pas ajouter quelque chose d'un type incorrect.

Autres types génériques

Dans Swift, certaines des structures les plus communes que vous utilisez sont des types génériques. Vous avez pris un coup d' oeil au Array ci-dessus. Dictionnaires et optionals sont également des types génériques et conduisent à des structures de données de type-safe.

Créez le dictionnaire suivant des codes de pays à la fin de votre terrain de jeu et puis recherchez le code de pays de l'Autriche:

  Let countryCodes = ["Autriche": "AT", "Etats-Unis d'Amérique": "US", "Turquie": "TR"] let at = countryCodes ["Austria"] 

Vérifiez les types des deux déclarations. Vous verrez que countryCodes est un dictionnaire de String clés et String de valeurs, rien d' autre ne peut jamais être dans ce dictionnaire. Le type générique formel est Dictionary .

Le type de at est String? , Qui est un raccourci pour en Optional , de sorte que vous pouvez voir que les types facultatifs sont des types génériques. Les génériques sont partout! Le compilateur impose que vous pouvez uniquement rechercher des clés de chaîne et que vous obtenez toujours des valeurs de chaîne. La valeur string est facultative, car il se peut qu'il n'y ait pas de valeur correspondant à la clé passée.

Ajoutez ce qui suit à votre aire de jeu pour voir la syntaxe explicite complète pour créer une chaîne facultative:

  Let optionalName = Facultatif <String> .Some ("John") if let name = optionalName {} 

Vérifiez le type de name , que vous voyez est String . En option de liaison, qui est, le if-let construction, est un opérateur générique de toutes sortes. Il prend une valeur de type T? et vous donne une valeur de type T .

Tutoriel Swift: Introduction aux génériques

Telles sont les principales idées derrière génériques. Pour les fonctions génériques, la liste des paramètres et / ou la valeur de retour dépendre d'un ou plusieurs types génériques, tels que T . Vous spécifiez ces types sur une base individuelle lorsque vous appelez la fonction, explicitement ou via l'inférence de type.

Maintenant que vous comprenez les bases des génériques, vous pouvez apprendre à écrire vos propres fonctions génériques et les types!

Écriture d'une fonction générique

Ajoutez la fonction suivante au bas de l'aire de jeu:

  (KeyType, ValueType)) -> [(KeyType, ValueType)] {retour Array (dictionnaire)} 

Regardez bien la déclaration de la fonction, la liste des paramètres et le type de retour.

La fonction est générique sur deux types que vous avez nommés KeyType et ValueType . Le seul paramètre est un dictionnaire de KeyType à ValueType , et la valeur de retour est un tableau de tuples de la forme-vous it- deviné (KeyType, ValueType) .

Vous pouvez utiliser pairsFromDictionary sur un dictionnaire valide et il va fonctionner, grâce à des médicaments génériques:

  Let pairs = pairsFromDictionary (["minimum": 199, "maximum": 299]) let plusPairs = pairsFromDictionary ([1: "Swift", 2: "Generics", 3: "Règle"]) 

Bien sûr, puisque vous ne pouvez pas contrôler l'ordre dans lequel les éléments du dictionnaire entrent dans le tableau, vous pouvez voir un ordre de valeurs de tuple dans votre aire de jeux plus comme "Generics", "Règle", "Swift", et en effet, ils Genre de faire! ......

Le compilateur génère des fonctions distinctes pour chaque possible KeyType et ValueType , remplissant les types réels dans la déclaration et le corps fonction. Dans le premier cas de pairs , pairsFromDictionary retourne un tableau de (String, Int) tuples, et dans le second cas de morePairs , elle retourne un tableau de (Int, String) tuples. Au niveau du code, vous avez créé les deux à l'aide d'une seule fonction.

Rédaction d'une structure générique de données

Une file d' attente est une structure comme une sorte de liste ou une pile de données, mais vous ne pouvez ajouter de nouvelles valeurs à la fin (mise en attente) et seulement prendre des valeurs de l'avant (dequeuing). Ce concept pourrait être familier si vous avez déjà utilisé NSOperationQueue ou Grand Central Dispatch, avec dispatch_async et les amis.

Ajouter ce qui suit struct déclaration à la fin de votre terrain de jeu:

  Struct Queue <Element> {} 

Queue est un type générique sur Element . Queue et la Queue d' Queue , par exemple, deviendront types de leur propre, générés par le compilateur, qui ne peut que enqueue et dequeue chaînes et entiers, respectivement.

Ajoutez la propriété privée suivante à la file d'attente:

  Private var elements = [Element] () 

Vous allez utiliser ce tableau pour contenir les éléments que vous initialisez en tant que tableau vide. Notez que vous pouvez utiliser l' Element comme si elle est un type réel, même si ça va être rempli plus tard.

Enfin, implémentez les deux méthodes de file d'attente principales:

  Muting func enqueue (newElement: Element) {éléments.append (newElement)} mutation func dequeue () -> Element?  {Guard! Elements.isEmpty autre {return nil} return elements.removeAtIndex (0)} 

Encore une fois, le paramètre de type Element est disponible partout dans le struct corps , y compris les méthodes à l' intérieur. Faire un type générique est comme rendre chaque de ses méthodes implicitement génériques sur le même type. Vous avez implémenté une structure de données génériques de type sécurisé, tout comme celles de la bibliothèque standard.

Jouez avec votre nouvelle structure de données pour un peu au fond de la cour de récréation.

  Var q = File d'attente <Int> () q.enqueue (4) q.enqueue (2) q.dequeue () q.dequeue () q.dequeue () q.dequeue () 

Aussi, amusez-vous en faisant intentionnellement autant d'erreurs que possible pour déclencher les différents messages d'erreur liés aux génériques - par exemple, ajoutez une chaîne à votre file d'attente. Plus vous en savez sur ces erreurs maintenant, plus il sera facile de reconnaître et de traiter avec eux plus tard.

Thèmes intermédiaires

Maintenant que vous connaissez les bases de la création et du travail avec des types génériques et des fonctions, il est temps de passer à certaines fonctionnalités plus avancées. Vous avez déjà vu comment génériques utiles sont de limiter les choses par type, mais vous pouvez ajouter des contraintes supplémentaires ainsi que d'étendre vos types génériques pour les rendre encore plus utile.

Contraintes de type et où les clauses

La fonction ci-dessous trie un tableau et trouve la valeur du milieu. Ajoutez-le à votre aire de jeux:

  (<Array>) -> T {return array.sort () [(array.count - 1) / 2]} 

Vous recevrez une erreur. Le problème est que pour sort() à travailler, les éléments du tableau doivent être Comparable . Vous devez dire en quelque sorte que Swift mid peut prendre tout tableau aussi longtemps que le type d'élément implémente Comparable .

Modifiez la déclaration de fonction comme suit:

  () () () () () () () () () () () () () () Array T: 

Ici, vous utilisez le where mot - clé pour ajouter une contrainte de type pour le type paramètre générique T . Vous pouvez maintenant appeler uniquement la fonction d'un ensemble d'éléments comparables, de sorte que sort() fonctionnera toujours! Essayez la fonction contrainte en ajoutant:

  Mid ([3, 5, 1, 2, 4]) // 3 

Dans le cas où where fait le type conforme à un protocole, comme ci - dessus, vous pouvez utiliser le sucre syntaxique suivant et supprimer where :

  <T: Comparable> (tableau: [T]) -> T {retour array.sort () [(array.count - 1) / 2]} 

D' autres utilisations de where sont quand il y a plus d'un type générique, et que vous voulez ajouter une contrainte entre deux ou plusieurs d'entre eux.

Maintenant que vous connaissez les contraintes de type, vous pouvez créer une version générique des fonctions d'addition depuis le début de la zone de jeu. Ajoutez le prototype et les extensions suivants à votre aire de jeux:

  Problème {} Extension {} Extension {} Explication: 

Tout d' abord, vous créez un Summable protocole qui dit tout type qui est conforme doivent avoir l'opérateur d'addition + disponible. Ensuite, vous indiquez que l' Int et Double types conforment.

Maintenant , en utilisant un type générique T et une contrainte de type, vous pouvez créer une fonction générique adder :

  (X: T, y: T) -> T {retour x + y} 

Vous avez réduit vos deux fonctions ( en fait plus, puisque vous auriez besoin de plus pour d' autres Summable types) à un seul et retiré du code redondant. Vous pouvez utiliser la nouvelle fonction sur les entiers et les doubles:

  Let adderIntSum = adder (1, 2) let adderDoubleSum = adder (1.0, 2.0) 

Et vous pouvez également l'utiliser sur d'autres types, tels que des chaînes:

  Extension String: Summable {} let adderString = additionneur ("Generics", "are Awesome !!!:]") 

En ajoutant d' autres types de conforme à Summable , votre adder(_:_:) fonction devient plus largement disponible grâce à sa définition des médicaments génériques à propulsion!

Extension d'un type générique

Prolonger la Queue d' Queue Queue de type et ajoutez la méthode suivante à droite en dessous de la Queue d' Queue définition:

  Extension Queue {func peek () -> Element?  {Return elements.first}} 

peek renvoie le premier élément sans dequeuing il. L'extension d'un type générique est facile! Le paramètre de type générique est visible comme dans le corps de la définition d'origine. Vous pouvez utiliser votre extension pour jeter un coup d'œil dans une file d'attente:

  Q.enqueue (5) q.enqueue (3) q.peek () 

Vous verrez la valeur 5 comme le premier élément dans la file d' attente, mais rien n'a été dequeued et la file d' attente a le même nombre d'éléments comme avant.

Défi: Etendre la Queue d' homogeneous Queue de type à mettre en œuvre une fonction homogeneous qui vérifie si tous les éléments de la file d' attente sont égaux. Vous aurez besoin d'ajouter une contrainte de type dans la Queue d' Queue déclaration pour que ses éléments peuvent être vérifiés pour l' égalité entre eux.

Tutoriel Swift: Introduction aux génériques

Solution à l'intérieur: file d'attente homogène SélectionnerShow

réponse:

  Struct Queue <Element: Equatable> { 

puis:

  Extension {func homogeneous () -> Bool {if let premier = elements.first {return! Elements.contains {$ 0! = First}} return true}} q.homogeneous () // false 

Nouvelles fonctionnalités génériques dans Swift 2

Avec de nombreuses autres fonctionnalités linguistiques, les génériques ont obtenu une belle mise à jour dans la dernière version de Swift 2. Ici, vous passerez en revue certaines des dernières mises à jour.

Sous-classement des classes génériques

Swift 2 a ajouté la possibilité de sous-classer des classes génériques, ce qui peut être utile dans certains cas. Auparavant, vous pouviez sous-classer des classes génériques, mais elles devaient également être génériques. Maintenant, vous pouvez faire une sous-classe plus "concrète" d'une classe générique.

Ajoutez la classe générique suivante à l'aire de jeux:

  Classe Boîte <T> {} 

Vous définissez une Box classe. La boîte peut contenir n'importe quoi, et c'est pourquoi c'est une classe générique. Il y a deux façons que vous pourriez sous - classement Box :

  1. Vous voudrez peut-être étendre ce que la boîte ne fonctionne et comment il fonctionne mais le garder générique, donc vous pouvez toujours mettre n'importe quoi dans la boîte;
  2. Vous pouvez avoir une sous-classe spécialisée qui sait toujours ce qu'il contient.

Swift 2 permet les deux. Ajouter ceci à votre aire de jeux:

  Classe Cadeau <T>: Boîte <T> {} classe StringBox: Boîte <String> {} 

Vous définissez deux Box sous - classes ici: Gift est un type spécial de la boîte qui sera vraisemblablement avoir différentes méthodes et propriétés définies sur elle, comme recipient et wrap() . Cependant, il a encore un générique sur le type, ce qui signifie qu'il pourrait contenir quoi que ce soit.

En revanche, Coffret StringBox est une boîte de cordes, et est donc pas générique plus.

Tutoriel Swift: Introduction aux génériques

Generics "chapitre et le" chapitre de référence de la langue du guide d'Apple, la programmation Swift Langue Paramètres et arguments génériques ". Vous trouverez des informations plus détaillées sur les génériques à Swift, ainsi que quelques exemples pratiques.

Nous vous remercions d'avoir pris le temps de vous renseigner sur les génériques à Swift, une fonctionnalité intégrale que vous utiliserez tous les jours pour écrire des abstractions puissantes et sécuritaires. Vous pouvez obtenir le meilleur des deux mondes - classes génériques, tout en spécifiant un type réel pour obtenir la sécurité de type lorsque vous l'instancier.

Si vous avez des questions, s'il vous plaît laissez-moi savoir dans la discussion forum ci-dessous! ......