Swift - Generici

Il linguaggio Swift 4 fornisce funzionalità "Generiche" per scrivere funzioni e tipi flessibili e riutilizzabili. I generici vengono utilizzati per evitare duplicazioni e fornire astrazioni. Le librerie standard di Swift 4 sono costruite con codice generico. I tipi "Arrays" e "Dictionary" di Swift 4s appartengono a raccolte generiche. Con l'aiuto di array e dizionari, gli array sono definiti per contenere valori "Int" e valori "String" o qualsiasi altro tipo.

func exchange(a: inout Int, b: inout Int) {
   let temp = a
   a = b
   b = temp
}

var numb1 = 100
var numb2 = 200

print("Before Swapping values are: \(numb1) and \(numb2)")
exchange(a: &numb1, b: &numb2)
print("After Swapping values are: \(numb1) and \(numb2)")

Quando eseguiamo il programma precedente usando playground, otteniamo il seguente risultato:

Before Swapping values are: 100 and 200
After Swapping values are: 200 and 100

Funzioni generiche: parametri di tipo

Le funzioni generiche possono essere utilizzate per accedere a qualsiasi tipo di dati come "Int" o "String".

func exchange<T>(a: inout T, b: inout T) {
   let temp = a
   a = b
   b = temp
}
var numb1 = 100
var numb2 = 200

print("Before Swapping Int values are: \(numb1) and \(numb2)")
exchange(a: &numb1, b: &numb2)
print("After Swapping Int values are: \(numb1) and \(numb2)")

var str1 = "Generics"
var str2 = "Functions"

print("Before Swapping String values are: \(str1) and \(str2)")
exchange(a: &str1, b: &str2)
print("After Swapping String values are: \(str1) and \(str2)")

Quando eseguiamo il programma precedente usando playground, otteniamo il seguente risultato:

Before Swapping Int values are: 100 and 200
After Swapping Int values are: 200 and 100
Before Swapping String values are: Generics and Functions
After Swapping String values are: Functions and Generics

La funzione exchange () è usata per scambiare i valori che è descritto nel programma sopra e <T> è usato come parametro di tipo. Per la prima volta, la funzione exchange () viene chiamata per restituire i valori "Int" e la seconda chiamata alla funzione exchange () restituirà i valori "String". È possibile includere più tipi di parametri all'interno delle parentesi angolari separate da virgole.

I parametri di tipo sono denominati come definiti dall'utente per conoscere lo scopo del parametro di tipo che contiene. Swift 4 fornisce <T> come nome di parametro di tipo generico. Tuttavia, i parametri di tipo come array e dizionari possono anche essere denominati come chiave, valore per identificare che appartengono al tipo "Dizionario".

struct TOS<T> {
   var items = [T]()
   mutating func push(item: T) {
      items.append(item)
   }
   mutating func pop() -> T {
      return items.removeLast()
   }
}

var tos = TOS<String>()
tos.push(item: "Swift 4")
print(tos.items)

tos.push(item: "Generics")
print(tos.items)

tos.push(item: "Type Parameters")
print(tos.items)

tos.push(item: "Naming Type Parameters")
print(tos.items)

let deletetos = tos.pop()

Quando eseguiamo il programma precedente usando playground, otteniamo il seguente risultato:

[Swift 4]
[Swift 4, Generics]
[Swift 4, Generics, Type Parameters]
[Swift 4, Generics, Type Parameters, Naming Type Parameters]

Estensione di un tipo generico

L'estensione della proprietà dello stack per conoscere la parte superiore dell'elemento è inclusa con la parola chiave "extension".

struct TOS<T> {
   var items = [T]()
   mutating func push(item: T) {
      items.append(item)
   }
   mutating func pop() -> T {
      return items.removeLast()
   }
}
var tos = TOS<String>()
tos.push(item: "Swift 4")
print(tos.items)

tos.push(item: "Generics")
print(tos.items)

tos.push(item: "Type Parameters")
print(tos.items)

tos.push(item: "Naming Type Parameters")
print(tos.items)

extension TOS {
   var first: T? {
      return items.isEmpty ? nil : items[items.count - 1]
   }
}
if let first = tos.first {
   print("The top item on the stack is \(first).")
}

Quando eseguiamo il programma precedente usando playground, otteniamo il seguente risultato:

["Swift 4"]
["Swift 4", "Generics"]
["Swift 4", "Generics", "Type Parameters"]
["Swift 4", "Generics", "Type Parameters", "Naming Type Parameters"]
The top item on the stack is Naming Type Parameters.

Vincoli di tipo

Il linguaggio Swift 4 consente ai "vincoli di tipo" di specificare se il parametro di tipo eredita da una classe specifica o di garantire lo standard di conformità del protocollo.

func exchange<T>(a: inout T, b: inout T) {
   let temp = a
   a = b
   b = temp
}
var numb1 = 100
var numb2 = 200

print("Before Swapping Int values are: \(numb1) and \(numb2)")
exchange(a: &numb1, b: &numb2)
print("After Swapping Int values are: \(numb1) and \(numb2)")

var str1 = "Generics"
var str2 = "Functions"

print("Before Swapping String values are: \(str1) and \(str2)")
exchange(a: &str1, b: &str2)
print("After Swapping String values are: \(str1) and \(str2)")

Quando eseguiamo il programma precedente usando playground, otteniamo il seguente risultato:

Before Swapping Int values are: 100 and 200
After Swapping Int values are: 200 and 100
Before Swapping String values are: Generics and Functions
After Swapping String values are: Functions and Generics

Tipi associati

Swift 4 consente di dichiarare i tipi associati all'interno della definizione del protocollo mediante la parola chiave "associatedtype".

protocol Container {
   associatedtype ItemType
   mutating func append(item: ItemType)
   var count: Int { get }
   subscript(i: Int) -> ItemType { get }
}
struct TOS<T>: Container {
   // original Stack<T> implementation
   var items = [T]()
   mutating func push(item: T) {
      items.append(item)
   }
   mutating func pop() -> T {
      return items.removeLast()
   }
   
   // conformance to the Container protocol
   mutating func append(item: T) {
      self.push(item: item)
   }
   var count: Int {
      return items.count
   }
   subscript(i: Int) -> T {
      return items[i]
   }
}
var tos = TOS<String>()
tos.push(item: "Swift 4")
print(tos.items)

tos.push(item: "Generics")
print(tos.items)

tos.push(item: "Type Parameters")
print(tos.items)

tos.push(item: "Naming Type Parameters")
print(tos.items)

Quando eseguiamo il programma precedente usando playground, otteniamo il seguente risultato:

[Swift 4]
[Swift 4, Generics]
[Swift 4, Generics, Type Parameters]
[Swift 4, Generics, Type Parameters, Naming Type Parameters]

Dove clausole

I vincoli di tipo consentono all'utente di definire requisiti sui parametri di tipo associati a una funzione o tipo generico. Per la definizione dei requisiti per i tipi associati, le clausole "where" sono dichiarate come parte dell'elenco dei parametri di tipo. La parola chiave 'where' viene posizionata immediatamente dopo l'elenco dei parametri di tipo seguito dai vincoli dei tipi associati, dalle relazioni di uguaglianza tra i tipi e dai tipi associati.

protocol Container {
   associatedtype ItemType
   mutating func append(item: ItemType)
   var count: Int { get }
   subscript(i: Int) -> ItemType { get }
}
struct Stack<T>: Container {
   // original Stack<T> implementation
   var items = [T]()
   mutating func push(item: T) {
      items.append(item)
   }
   mutating func pop() -> T {
      return items.removeLast()
   }

   // conformance to the Container protocol
   mutating func append(item: T) {
      self.push(item: item)
   }
   var count: Int {
      return items.count
   }
   subscript(i: Int) -> T {
      return items[i]
   }
}
func allItemsMatch<
C1: Container, C2: Container
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
(someContainer: C1, anotherContainer: C2) -> Bool {
   // check that both containers contain the same number of items
   if someContainer.count != anotherContainer.count {
      return false
   }
   
   // check each pair of items to see if they are equivalent
   for i in 0..<someContainer.count {
      if someContainer[i] != anotherContainer[i] {
         return false
      }
   }
   // all items match, so return true
   return true
}  
var tos = Stack<String>()

tos.push(item: "Swift 4")
print(tos.items)

tos.push(item: "Generics")
print(tos.items)

tos.push(item: "Where Clause")
print(tos.items)

var eos = ["Swift 4", "Generics", "Where Clause"]
print(eos)

Quando eseguiamo il programma precedente usando playground, otteniamo il seguente risultato:

[Swift 4]
[Swift 4, Generics]
[Swift 4, Generics, Where Clause]
[Swift 4, Generics, Where Clause]