English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Swift ガジェット

Swift は汎型を提供し、柔軟で再利用可能な関数や型を書くことができます。

Swift の標準ライブラリは汎型コードで構築されています。

Swift の配列や辞書型は汎型集合です。

Int 配列、String 配列、または他の Swift の型データ配列を作成できます。

以下の例は、2つの Int 値を交換するための非汎型関数 exchange です:

オンラインサンプル

// 二つの変数を交換する関数を定義します
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}
 
var numb1 = 100
var numb2 = 200
 
print("交換前のデータ: \(numb1) と \(numb2)")
swapTwoInts(&numb1, \&numb2)
print("交換後のデータ: \(numb1) と \(numb2)")

以下のプログラムの実行結果は:

交換前のデータ: 100 と 200
交換後のデータ: 200 と 100

上記の例は、整数 Int 型の変数の交換にのみ適用されます。String 値や Double 値を交換したい場合は、swapTwoStrings(_:_:) または swapTwoDoubles(_:_:) などの対応する関数を再書きする必要があります。以下のように示されます:

String と Double 値の交換関数

func swapTwoStrings(_ a: inout String, _ b: inout String) {
    let temporaryA = a
    a = b
    b = temporaryA
}
 
func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
    let temporaryA = a
    a = b
    b = temporaryA
}

上記のコードを見ると、機能コードは同じですが、タイプが異なります。この場合、ジェネリックを使用してコードの重複を避けることができます。

ジェネリックは占位符のタイプ名(ここではTを使用しています)を使用して実際のタイプ名(例えばInt、String、またはDouble)を代用します。

func swapTwoValues<T>(_ a: inout T, _ b: inout T)

swapTwoValuesの後ろに占位符のタイプ名(T)が続き、括弧で括られています(<T>)。この尖括号は、SwiftにTがswapTwoValues(_:_:)関数定義内の占位符のタイプ名であることを示しています。したがって、SwiftはTとしての実際のタイプを探しません。

以下の例は、IntとStringの値を交換するジェネリック関数exchangeです:

オンラインサンプル

// 二つの変数を交換する関数を定義します
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}
 
var numb1 = 100
var numb2 = 200
 
print("交換前のデータ: \(numb1) と \(numb2)")
swapTwoValues(&numb1, \&numb2)
print("交換後のデータ: \(numb1) と \(numb2)")
 
var str1 = \"A\"
var str2 = \"B\"
 
print("交換前のデータ: \(str1) と \(str2)")
swapTwoValues(&str1, \&str2)
print("交換後のデータ: \(str1) と \(str2)")

以下のプログラムの実行結果は:

交換前のデータ:  100 と 200
交換後のデータ: 200 と 100
交換前のデータ: A と B
交換後のデータ: B と A

ジェネリックタイプ

Swiftは、あなた自身のジェネリックタイプを定義することができます。

カスタムクラス、構造体、エnumはArrayやDictionaryの使い方と同じようにどんなタイプにも適用できます。

次に、Stack(スタック)と呼ばれるジェネリックコレクションタイプを作成しましょう。スタックはコレクションの末端に新しい要素を追加する(スタックインと呼ばれます)のみで、末端から要素を取り除く(スタックアウトと呼ばれます)こともできます。

画像は左から右に以下のように解釈されます:

  • スタックには3つの値があります。

  • 4番目の値がスタックの顶部に圧入されました。

  • 現在、スタックには4つの値があり、最も最近にスタックに追加された値が顶部にあります。

  • スタックの一番上の値が取り除かれた、または出力されることを言います。

  • 値を取り除いた後、スタックには再び3つの値しか残っていません。

以下は非泛型のスタックの例です。Int 型のスタックとして示します:

Int 型のスタック

struct IntStack {
    var items = [Int]()
    mutating func push(_ item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }
}

この構造体は、値を格納するために items という名前の Array 属性を使用しています。Stack は push(_: ) と pop() の2つのメソッドを提供しており、これらのメソッドは items 配列を変更するため、mutating とされています。

上の IntStack 構造体は Int 型のみを使用できます。ただし、任意の値を処理できる泛型の Stack 構造体を定義することができます。

以下は同じコードの泛型バージョンです:

泛型のスタック

struct Stack<Element> {
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}
 
var stackOfStrings = Stack<String>()
print("文字列要素がスタックに格納されます:")
stackOfStrings.push("google")
stackOfStrings.push("w3codebox)
print(stackOfStrings.items);
 
let deletetos = stackOfStrings.pop()
print("出力される要素: " + deletetos)
 
var stackOfInts = Stack<Int>()
print("整数要素をスタックに追加: ")
stackOfInts.push(1)
stackOfInts.push(2)
print(stackOfInts.items);

実行結果は以下の通りです:

文字列要素がスタックに格納されます: 
["google", "w3codebox"]
スタックから出力される要素: w3codebox
整数要素をスタックに追加: 
[1, 2]

Stack は基本的に IntStack と同じで、実際の Int 型に代わる占位型引数 Element が使用されています。

以下の例では、Element が以下の3か所で占位符として使用されています:

  • を作成します。 items 属性、 Element の空の配列で初期化します。

  • 指定 push(_:) メソッドのユニークな引数 item の型は、 Element 型。

  • 指定 pop() メソッドの返却値型は、 Element 型。

拡張泛型型

拡張する泛型型(extension キーワードを使用)のとき、拡張の定義では型引数リストを提供する必要はありません。もっと便利なのは、元の型定義で宣言された型引数リストが拡張では使用できるため、これらの引数名が元の定義の型引数の参照として使用されます。

以下の例では、泛型型Stackを拡張し、読み取り専用の計算型プロパティtopItemを追加します。これは現在のスタックの先頭要素を返しますが、それをスタックから取り除きません:

汎型

struct Stack<Element> {
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}
 
extension Stack {
    var topItem: Element? {
       return items.isEmpty ? nil : items[items.count - 1]
    }
}
 
var stackOfStrings = Stack<String>()
print("文字列要素がスタックに格納されます:")
stackOfStrings.push("google")
stackOfStrings.push("w3codebox)
 
if let topItem = stackOfStrings.topItem {
    print("スタックのトップ要素は:\(topItem).")
}
 
print(stackOfStrings.items)

この例では、topItemプロパティはElement型のオプション値を返します。スタックが空の場合、topItemはnilを返します;スタックが空でない場合、topItemはitems配列の最後の要素を返します。

以下のプログラムの実行結果は:

文字列要素がスタックに格納されます: 
スタックのトップ要素は:w3codebox.
["google", "w3codebox"]

既存の型を拡張することで関連型を指定することもできます。

例えば、SwiftのArray型は、append(_: )メソッド、count属性、Int型のインデックス値を受け入れるインデックスを使用して要素を取得する機能を提供しています。これらの機能はすべてContainerプロトコルの要件を満たすため、Arrayがそのプロトコルを采纳するだけで簡単に拡張できます。

以下の例では、空の拡張を作成するだけで十分です:

extension Array: Container {}

型制約

型制約は、指定されたクラスを継承する必要がある型パラメータ、または特定のプロトコルやプロトコルの構成に従う必要があることを指定します。

型制約文法

一つの型パラメータ名の後ろに、カンマで区切って型制約を指定することができます。これは型パラメータチェーンの一部として機能します。以下は、この泛型関数に適用される型制約の基本的な文法です(型の文法と同じです):

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // 这里是泛型函数的函数体部分
}

上面这个函数有两个类型参数。第一个类型参数 T,有一个要求 T 必须是 SomeClass 子类的类型约束;第二个类型参数 U,有一个要求 U 必须符合 SomeProtocol 协议的类型约束。

オンラインサンプル

汎型

// 非泛型函数,查找指定字符串在数组中的索引
func findIndex(ofString valueToFind: String, in array: [String]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            // 找到返回索引值
            return index
        }
    }
    
}
 
 
3codebox
if let foundIndex = findIndex(ofString: "w3codebox
    print("w3codebox のインデックスは (foundIndex) です)
}

インデックスのインデックスは 0 から始まります。

以下のプログラムの実行結果は:

w3codebox のインデックスは 3

関連クラス

Swift では associatedtype キーワードを使用して関連型の例を設定します。

以下の例では、Container 協定を定義し、その協定には関連型 ItemType が定義されています。

Container 協定は、Container 協定に従う型が提供する必要がある機能を3つだけ指定します。これら3つの条件を満たす場合、従う型は追加の機能も提供できます。

// Containerプロトコル
protocol Container {
    associatedtype ItemType
    // 新しい要素をコンテナに追加します
    mutating func append(_ item: ItemType)
    // コンテナ内の要素数を取得します
    var count: Int { get }
    // インデックス値Intのタイプで、コンテナ内の各要素を検索します
    subscript(i: Int) -> ItemType { get }
}
// Stack 構造体は Container 協定に従います
struct Stack<Element>: Container {
    // Stack<Element>のオリジナルの実装部分
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
    // Container プロトコルの実装部分
    mutating func append(_ item: Element) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Element {
        return items[i]
    }
}
var tos = Stack<String>()
tos.push("google")
tos.push("w3codebox)
tos.push("taobao")
// 要素リスト
print(tos.items)
// 要素の数
print( tos.count)

以下のプログラムの実行結果は:

["google", "w3codebox, "taobao"]
3

Where 文

型制約は、型がジェネリック関数やクラスの定義制約に従っていることを保証します。

パラメータリスト内で where 文を使用してパラメータの制約を定義できます。

型パラメータリストの後ろに続いて where 文を書くことができます。where 文の後には、関連型に対する制約が1つまたは複数指定され、または(または)型と関連型間の等価(等価)関係が1つまたは複数指定されます。

オンラインサンプル

以下の例では、allItemsMatchという名前の汎型関数が定義されており、二つのContainer例が同じ順序の同じ要素を含んでいるかをチェックします。

すべての要素が一致する場合、trueを返し、そうでない場合、falseを返します。

汎型

// Containerプロトコル
protocol Container {
    associatedtype ItemType
    // 新しい要素をコンテナに追加します
    mutating func append(_ item: ItemType)
    // コンテナ内の要素数を取得します
    var count: Int { get }
    // インデックス値Intのタイプで、コンテナ内の各要素を検索します
    subscript(i: Int) -> ItemType { get }
}
 
// // Containerプロトコルに従う汎型TOSタイプ
struct Stack<Element>: Container {
    // Stack<Element>のオリジナルの実装部分
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
    // Container プロトコルの実装部分
    mutating func append(_ item: Element) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Element {
        return items[i]
    }
}
// 拡張して、ArrayをContainerとして使用します
extension Array: Container {}
 
func allItemsMatch<C1: Container, C2: Container>
    (_ someContainer: C1, _ anotherContainer: C2) -> Bool
    where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
        
        // チェックする二つのコンテナが同じ要素数を持っているか確認します
        if someContainer.count != anotherContainer.count {
            return false
        }
        
        // 各ペアの要素が一致するか確認する
        for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            }
        }
        
        // すべての要素が一致し、trueを返す
        return true
}
var tos = Stack<String>()
tos.push("google")
tos.push("w3codebox)
tos.push("taobao")
 
var aos = ["google", "w3codebox, "taobao"]
 
if allItemsMatch(tos, aos) {
    print("すべての要素にマッチ")
} else {
    print("要素が一致しません")
}

以下のプログラムの実行結果は:

すべての要素にマッチ