汎用
日本語を消す 英語を消す下記URLから引用し、日本語訳をつけてみました。
https://docs.swift.org/swift-book/documentation/the-swift-programming-language/generics
Write code that works for multiple types and specify requirements for those types.
複数の型で機能するコードを作成し、それらの型の要件を指定します。
Generic code enables you to write flexible, reusable functions and types that can work with any type, subject to requirements that you define. You can write code that avoids duplication and expresses its intent in a clear, abstracted manner.
汎用コードを使用すると、定義した要件に従って、あらゆる型で動作する、柔軟で再利用可能な関数と型を作成できます。 重複を避け、明確かつ抽象化された方法でその意図を表現するコードを作成できます。
Generics are one of the most powerful features of Swift, and much of the Swift standard library is built with generic code. In fact, you’ve been using generics throughout the Language Guide, even if you didn’t realize it. For example, Swift’s Array
and Dictionary
types are both generic collections. You can create an array that holds Int
values, or an array that holds String
values, or indeed an array for any other type that can be created in Swift. Similarly, you can create a dictionary to store values of any specified type, and there are no limitations on what that type can be.
ジェネリックは Swift の最も強力な機能の 1 つであり、Swift 標準ライブラリの多くはジェネリック コードで構築されています。 実際、たとえ気づかなかったとしても、言語ガイド全体でジェネリックを使用しています。 たとえば、Swift の Array
型と Dictionary
型はどちらも汎用コレクションです。 Int
値を保持する配列、String
値を保持する配列、あるいは Swift で作成できるその他の型の配列を作成できます。 同様に、指定した型の値を格納するディクショナリを作成できます。その型に制限はありません。
The Problem That Generics Solve
ジェネリックが解決する問題
Here’s a standard, nongeneric function called swapTwoInts(_:_:)
, which swaps two Int
values:
これは swapTwoInts(_:_:)
という標準の非ジェネリック関数で、2 つの Int
値を交換します。
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
This function makes use of in-out parameters to swap the values of a
and b
, as described in In-Out Parameters.
この関数は、「In-Out パラメータ」で説明されているように、in-out パラメータを利用して a
と b
の値を交換します。
The swapTwoInts(_:_:)
function swaps the original value of b
into a
, and the original value of a
into b
. You can call this function to swap the values in two Int
variables:
swapTwoInts(_:_:)
関数は、b
の元の値を a
に、a
の元の値を b
に交換します。 この関数を呼び出して、2 つの Int
変数の値を交換できます。
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// Prints "someInt is now 107, and anotherInt is now 3"
The swapTwoInts(_:_:)
function is useful, but it can only be used with Int
values. If you want to swap two String
values, or two Double
values, you have to write more functions, such as the swapTwoStrings(_:_:)
and swapTwoDoubles(_:_:)
functions shown below:
swapTwoInts(_:_:)
関数は便利ですが、Int
値でのみ使用できます。 2 つの String
値、または 2 つの Double
値を交換する場合は、以下に示す swapTwoStrings(_:_:)
関数や swapTwoDoubles(_:_:)
関数など、さらに関数を記述する必要があります。
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
}
You may have noticed that the bodies of the swapTwoInts(_:_:)
, swapTwoStrings(_:_:)
, and swapTwoDoubles(_:_:)
functions are identical. The only difference is the type of the values that they accept (Int
, String
, and Double
).
swapTwoInts(_:_:)
、swapTwoStrings(_:_:)
、swapTwoDoubles(_:_:)
関数の本体が同一であることに気づいたかもしれません。 唯一の違いは、受け入れる値の型 (Int
、String
、Double
) です。
It’s more useful, and considerably more flexible, to write a single function that swaps two values of any type. Generic code enables you to write such a function. (A generic version of these functions is defined below.)
任意の型の 2 つの値を交換する単一の関数を作成する方が便利で、かなり柔軟です。 汎用コードを使用すると、このような関数を作成できます。 (これらの関数の汎用バージョンは以下で定義されています。)
Note
注釈
In all three functions, the types of a
and b
must be the same. If a
and b
aren’t of the same type, it isn’t possible to swap their values. Swift is a type-safe language, and doesn’t allow (for example) a variable of type String
and a variable of type Double
to swap values with each other. Attempting to do so results in a compile-time error.
3 つの関数すべてで、a
と b
の型は同じである必要があります。 a
と b
が同じ型ではない場合、それらの値を交換することはできません。 Swift は型セーフな言語であり、(たとえば)String
型の変数と Double
型の変数の値を相互に交換することはできません。 これを実行しようとすると、コンパイル時エラーが発生します。
Generic Functions
汎用関数
Generic functions can work with any type. Here’s a generic version of the swapTwoInts(_:_:)
function from above, called swapTwoValues(_:_:)
:
汎用関数はどの型でも機能します。 以下は、上記の swapTwoInts(_:_:)
関数の汎用バージョンで、swapTwoValues(_:_:)
と呼ばれます。
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
The body of the swapTwoValues(_:_:)
function is identical to the body of the swapTwoInts(_:_:)
function. However, the first line of swapTwoValues(_:_:)
is slightly different from swapTwoInts(_:_:)
. Here’s how the first lines compare:
swapTwoValues(_:_:)
関数の本体は、swapTwoInts(_:_:)
関数の本体と同じです。 ただし、swapTwoValues(_:_:)
の最初の行は swapTwoInts(_:_:)
とは少し異なります。 最初の行を比較すると次のようになります。
func swapTwoInts(_ a: inout Int, _ b: inout Int)
func swapTwoValues<T>(_ a: inout T, _ b: inout T)
The generic version of the function uses a placeholder type name (called T
, in this case) instead of an actual type name (such as Int
, String
, or Double
). The placeholder type name doesn’t say anything about what T
must be, but it does say that both a
and b
must be of the same type T
, whatever T
represents. The actual type to use in place of T
is determined each time the swapTwoValues(_:_:)
function is called.
この関数のジェネリック バージョンでは、実際の型名 (Int
、String
、Double
など) の代わりに、プレースホルダー型名 (この場合は T
) が使用されます。 プレースホルダーの型名は、T
が何でなければならないかについては何も示していませんが、T
が何を表すかに関係なく、a
と b
の両方が同じ型 T
である必要があることを示しています。 T
の代わりに使用する実際の型は、swapTwoValues(_:_:)
関数が呼び出されるたびに決定されます。
The other difference between a generic function and a nongeneric function is that the generic function’s name (swapTwoValues(_:_:)
) is followed by the placeholder type name (T
) inside angle brackets (<T>
). The brackets tell Swift that T
is a placeholder type name within the swapTwoValues(_:_:)
function definition. Because T
is a placeholder, Swift doesn’t look for an actual type called T
.
ジェネリック関数と非ジェネリック関数のもう 1 つの違いは、ジェネリック関数の名前 (swapTwoValues(_:_:)
) の後に山括弧(<T>
)内のプレースホルダー型名 (T
) が続くことです。 山括弧は、T
が swapTwoValues(_:_:)
関数定義内のプレースホルダー型名であることを Swift に伝えます。 T
はプレースホルダーであるため、Swift は T
という実際の型を探しません。
The swapTwoValues(_:_:)
function can now be called in the same way as swapTwoInts
, except that it can be passed two values of any type, as long as both of those values are of the same type as each other. Each time swapTwoValues(_:_:)
is called, the type to use for T
is inferred from the types of values passed to the function.
swapTwoValues(_:_:)
関数は、swapTwoInts
と同じ方法で呼び出すことができるようになりました。ただし、両方の値が互いに同じ型である限り、任意の型の 2 つの値を渡すことができる点が異なります。 swapTwoValues(_:_:)
が呼び出されるたびに、関数に渡された値の型から T
に使用する型が推測されます。
In the two examples below, T
is inferred to be Int
and String
respectively:
以下の 2 つの例では、T
はそれぞれ Int
と String
であると推測されます。
var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
// someInt is now 107, and anotherInt is now 3
var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
// someString is now "world", and anotherString is now "hello"
Note
注釈
The swapTwoValues(_:_:)
function defined above is inspired by a generic function called swap
, which is part of the Swift standard library, and is automatically made available for you to use in your apps. If you need the behavior of the swapTwoValues(_:_:)
function in your own code, you can use Swift’s existing swap(_:_:)
function rather than providing your own implementation.
上記で定義された swapTwoValues(_:_:)
関数は、Swift 標準ライブラリの一部である swap
と呼ばれる汎用関数からインスピレーションを得ており、アプリで使用できるように自動的に利用可能になります。 独自のコードで swapTwoValues(_:_:)
関数の動作が必要な場合は、独自の実装を提供するのではなく、Swift の既存の swap(_:_:)
関数を使用できます。
Type Parameters
型パラメータ
In the swapTwoValues(_:_:)
example above, the placeholder type T
is an example of a type parameter. Type parameters specify and name a placeholder type, and are written immediately after the function’s name, between a pair of matching angle brackets (such as <T>
).
上記の swapTwoValues(_:_:)
の例では、プレースホルダ型 T
が型パラメータの例です。 型パラメーターは、プレースホルダー型を指定して名前を付け、関数名の直後、一致する一対の山括弧 ( <T>
など) の間に記述されます。
Once you specify a type parameter, you can use it to define the type of a function’s parameters (such as the a
and b
parameters of the swapTwoValues(_:_:)
function), or as the function’s return type, or as a type annotation within the body of the function. In each case, the type parameter is replaced with an actual type whenever the function is called. (In the swapTwoValues(_:_:)
example above, T
was replaced with Int
the first time the function was called, and was replaced with String
the second time it was called.)
型パラメータを指定すると、それを使用して関数のパラメータの型(swapTwoValues(_:_:)
関数の a
パラメータと b
パラメータなど)を定義したり、関数の戻り値の型や型として定義したりできます。 関数本体内のアノテーション。 いずれの場合も、関数が呼び出されるたびに、型パラメータは実際の型に置き換えられます。 (上記の swapTwoValues(_:_:)
の例では、関数が初めて呼び出されたときに T
が Int
に置き換えられ、2 回目に呼び出されたときに String
に置き換えられました。)
You can provide more than one type parameter by writing multiple type parameter names within the angle brackets, separated by commas.
複数の型パラメータ名を山括弧内にカンマで区切って記述することにより、複数の型パラメータを指定できます。
Naming Type Parameters
名前付け型パラメータ
In most cases, type parameters have descriptive names, such as Key
and Value
in Dictionary<Key, Value>
and Element
in Array<Element>
, which tells the reader about the relationship between the type parameter and the generic type or function it’s used in. However, when there isn’t a meaningful relationship between them, it’s traditional to name them using single letters such as T
, U
, and V
, such as T
in the swapTwoValues(_:_:)
function above.
ほとんどの場合、型パラメータには、Dictionary
の Key
と Value
、Array
の Element
など、わかりやすい名前が付いています。これにより、型パラメータと、それが使用されるジェネリック型または関数との関係が読者にわかります。 ただし、それらの間に意味のある関係がない場合は、上記の swapTwoValues(_:_:)
関数の T
のように、T
、U
、V
などの 1 文字を使用して名前を付けるのが伝統的です。
Note
注釈
Always give type parameters upper camel case names (such as T
and MyTypeParameter
) to indicate that they’re a placeholder for a type, not a value.
型パラメータには常に大文字のキャメルケース名(T や MyTypeParameter など)を付けて、値ではなく型のプレースホルダであることを示します。
Generic Types
ジェネリック型
In addition to generic functions, Swift enables you to define your own generic types. These are custom classes, structures, and enumerations that can work with any type, in a similar way to Array
and Dictionary
.
ジェネリック関数に加えて、Swift では独自のジェネリック 型を定義できます。 これらは、Array
やDictionary
と同様の方法で、任意の型を処理できるカスタム クラス、構造、列挙です。
This section shows you how to write a generic collection type called Stack
. A stack is an ordered set of values, similar to an array, but with a more restricted set of operations than Swift’s Array
type. An array allows new items to be inserted and removed at any location in the array. A stack, however, allows new items to be appended only to the end of the collection (known as pushing a new value on to the stack). Similarly, a stack allows items to be removed only from the end of the collection (known as popping a value off the stack).
このセクションでは、Stack
と呼ばれる汎用コレクション 型を作成する方法を説明します。 スタックは、配列に似た順序付けられた値のセットですが、Swift の Array
型よりも操作のセットが制限されています。 配列を使用すると、配列内の任意の場所で新しい項目を挿入および削除できます。 ただし、スタックでは、新しい項目をコレクションの末尾にのみ追加できます(新しい値をスタックにプッシュすると呼ばれます)。 同様に、スタックでは、コレクションの最後からのみ項目を削除できます(スタックから値をポップすると呼ばれます)。
Note
注釈
The concept of a stack is used by the UINavigationController
class to model the view controllers in its navigation hierarchy. You call the UINavigationController
class pushViewController(_:animated:)
method to add (or push) a view controller on to the navigation stack, and its popViewControllerAnimated(_:)
method to remove (or pop) a view controller from the navigation stack. A stack is a useful collection model whenever you need a strict “last in, first out” approach to managing a collection.
スタックの概念は、UINavigationController
クラスによってナビゲーション階層内のビュー コントローラーをモデル化するために使用されます。 UINavigationController
クラスのpushViewController(_:animated:)
メソッドを呼び出してビューコントローラをナビゲーションスタックに追加(またはプッシュ)し、そのpopViewControllerAnimated(_:)
メソッドを呼び出してビューコントローラをナビゲーションスタックから削除(またはポップ)します。 スタックは、厳密な「後入れ先出し」アプローチでコレクションを管理する必要がある場合に便利なコレクション モデルです。
The illustration below shows the push and pop behavior for a stack:
以下の図は、スタックのプッシュおよびポップの動作を示しています。
![](https://docs.swift.org/swift-book/images/stackPushPop@2x.png)
- 1.There are currently three values on the stack.
- 1.現在スタックには 3 つの値があります。
- 2.A fourth value is pushed onto the top of the stack.
- 2.4 番目の値がスタックの一番上にプッシュされます。
- 3.The stack now holds four values, with the most recent one at the top.
- 3.スタックには 4 つの値が保持され、最新の値が一番上になります。
- 4.The top item in the stack is popped.
- 4.スタックの一番上の項目がポップされます。
- 5.After popping a value, the stack once again holds three values.
- 5.値をポップした後、スタックは再び 3 つの値を保持します。
Here’s how to write a nongeneric version of a stack, in this case for a stack of Int
values:
スタックの非ジェネリック バージョンを記述する方法は次のとおりです。この場合は、Int
値のスタックの場合です。
struct IntStack {
var items: [Int] = []
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
}
This structure uses an Array
property called items
to store the values in the stack. Stack
provides two methods, push
and pop
, to push and pop values on and off the stack. These methods are marked as mutating
, because they need to modify (or mutate) the structure’s items
array.
この構造では、items
と呼ばれる Array
プロパティを使用して値をスタックに保存します。 Stack
には、スタックに値をプッシュしたりスタックから値をポップしたりするための 2 つのメソッド、プッシュとポップが用意されています。 これらのメソッドは、構造体のitems
配列を変更(または変更)する必要があるため、mutating
としてマークされています。
The IntStack
type shown above can only be used with Int
values, however. It would be much more useful to define a generic Stack
structure, that can manage a stack of any type of value.
ただし、上記の IntStack
型は Int
値でのみ使用できます。 あらゆる型の値のスタックを管理できる、一般的なスタック構造を定義する方がはるかに便利です。
Here’s a generic version of the same code:
同じコードの汎用バージョンを次に示します。
struct Stack<Element> {
var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
Note how the generic version of Stack
is essentially the same as the nongeneric version, but with a type parameter called Element
instead of an actual type of Int
. This type parameter is written within a pair of angle brackets (<Element>
) immediately after the structure’s name.
Stack
のジェネリック バージョンは非ジェネリック バージョンと本質的に同じですが、実際の型の Int
の代わりに Element
と呼ばれる型パラメータが付いていることに注目してください。 この型パラメータは、構造体の名前の直後に一対の山括弧 (<Element>
) 内に記述されます。
Element
defines a placeholder name for a type to be provided later. This future type can be referred to as Element
anywhere within the structure’s definition. In this case, Element
is used as a placeholder in three places:
Element
は、後で提供される型のプレースホルダー名を定義します。 この将来型は、構造の定義内のどこでもElement
として参照できます。 この場合、Element
は次の 3 つの場所でプレースホルダとして使用されます。
- To create a property called
items
, which is initialized with an empty array of values of typeElement
items
という名前のプロパティを作成するには、Element
型の値の空の配列で初期化します。- To specify that the
push(_:)
method has a single parameter calleditem
, which must be of typeElement
push(_:)
メソッドにitem
という単一のパラメータがあることを指定するには、そのパラメータの型はElement
である必要があります- To specify that the value returned by the
pop()
method will be a value of typeElement
pop()
メソッドによって返される値がElement
型の値になるように指定する.。
Because it’s a generic type, Stack
can be used to create a stack of any valid type in Swift, in a similar manner to Array
and Dictionary
.
これはジェネリック型であるため、Stack
を使用すると、Array
や Dictionary
と同様の方法で、Swift で任意の有効な型のスタックを作成できます。
You create a new Stack
instance by writing the type to be stored in the stack within angle brackets. For example, to create a new stack of strings, you write Stack<String>()
:
新しいStack
インスタンスを作成するには、スタックに保存する型を山かっこ内に記述します。 たとえば、文字列の新しいスタックを作成するには、Stack<String>()
と記述します。
var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
stackOfStrings.push("cuatro")
// the stack now contains 4 strings
Here’s how stackOfStrings
looks after pushing these four values on to the stack:
これら 4 つの値をスタックにプッシュした後の stackOfStrings
は次のようになります。
![](https://docs.swift.org/swift-book/images/stackPushedFourStrings@2x.png)
Popping a value from the stack removes and returns the top value, "cuatro"
:
スタックから値をポップすると、最上位の値「cuatro
」が削除されて返されます。
let fromTheTop = stackOfStrings.pop()
// fromTheTop is equal to "cuatro", and the stack now contains 3 strings
Here’s how the stack looks after popping its top value:
スタックの最上位値をポップした後のスタックの様子は次のとおりです。
![](https://docs.swift.org/swift-book/images/stackPoppedOneString@2x.png)
Extending a Generic Type
ジェネリック型の拡張
When you extend a generic type, you don’t provide a type parameter list as part of the extension’s definition. Instead, the type parameter list from the original type definition is available within the body of the extension, and the original type parameter names are used to refer to the type parameters from the original definition.
ジェネリック型を拡張する場合、拡張機能の定義の一部として型パラメーター リストを提供しません。 代わりに、元の型定義の型パラメータ リストが拡張機能の本文内で使用でき、元の型パラメータ名は元の定義の型パラメータを参照するために使用されます。
The following example extends the generic Stack
type to add a read-only computed property called topItem
, which returns the top item on the stack without popping it from the stack:
次の例では、汎用のStack
型を拡張して、topItem
という読み取り専用の計算プロパティを追加します。このプロパティは、スタックからポップせずにスタックの一番上の項目を返します。
extension Stack {
var topItem: Element? {
return items.isEmpty ? nil : items[items.count - 1]
}
}
The topItem
property returns an optional value of type Element
. If the stack is empty, topItem
returns nil
; if the stack isn’t empty, topItem
returns the final item in the items
array.
topItem
プロパティは、型 Element
のオプションの値を返します。 スタックが空の場合、topItem
は nil
を返します。 スタックが空でない場合、topItem
は items
配列の最後のアイテムを返します。
Note that this extension doesn’t define a type parameter list. Instead, the Stack
type’s existing type parameter name, Element
, is used within the extension to indicate the optional type of the topItem
computed property.
この拡張機能は型パラメータのリストを定義しないことに注意してください。 代わりに、Stack
型の既存の型 パラメータ名である Element
が拡張機能内で使用され、topItem
計算プロパティのオプションの型を示します。
The topItem
computed property can now be used with any Stack
instance to access and query its top item without removing it.
topItem
計算プロパティを任意のStack
インスタンスで使用して、削除せずにその最上位アイテムにアクセスしてクエリできるようになりました。
if let topItem = stackOfStrings.topItem {
print("The top item on the stack is \(topItem).")
}
// Prints "The top item on the stack is tres."
Extensions of a generic type can also include requirements that instances of the extended type must satisfy in order to gain the new functionality, as discussed in Extensions with a Generic Where Clause below.
ジェネリック型の拡張子には、下方の 汎用Where 句を使用した拡張子で説明するように、新しい機能を得るために拡張型のインスタンスが満たさなければならない要件を含めることもできます。
Type Constraints
型の制約
The swapTwoValues(_:_:)
function and the Stack
type can work with any type. However, it’s sometimes useful to enforce certain type constraints on the types that can be used with generic functions and generic types. Type constraints specify that a type parameter must inherit from a specific class, or conform to a particular protocol or protocol composition.
swapTwoValues(_:_:)
関数とStack
型は、どの型でも機能します。 ただし、ジェネリック関数やジェネリック型で使用できる型に特定の型制約を適用すると便利な場合があります。 型制約は、型パラメーターが特定のクラスから継承するか、特定のプロトコルまたはプロトコル構成に準拠する必要があることを指定します。
For example, Swift’s Dictionary
type places a limitation on the types that can be used as keys for a dictionary. As described in Dictionaries, the type of a dictionary’s keys must be hashable. That is, it must provide a way to make itself uniquely representable. Dictionary
needs its keys to be hashable so that it can check whether it already contains a value for a particular key. Without this requirement, Dictionary
couldn’t tell whether it should insert or replace a value for a particular key, nor would it be able to find a value for a given key that’s already in the dictionary.
たとえば、Swift の辞書型では、Dictionary
のキーとして使用できる型に制限が設けられています。 「辞書」で説明されているように、Dictionary
のキーの型はハッシュ可能である必要があります。 つまり、それ自体を一意に表現できるようにする方法を提供する必要があります。 Dictionary
には、特定のキーの値が既に含まれているかどうかを確認できるように、キーがハッシュ可能である必要があります。 この要件がないと、Dictionary
は特定のキーの値を挿入するか置換するかを判断できず、すでに辞書に存在する特定のキーの値を見つけることもできません。
This requirement is enforced by a type constraint on the key type for Dictionary
, which specifies that the key type must conform to the Hashable
protocol, a special protocol defined in the Swift standard library. All of Swift’s basic types (such as String
, Int
, Double
, and Bool
) are hashable by default. For information about making your own custom types conform to the Hashable
protocol, see Conforming to the Hashable Protocol(Link:developer.apple.com).
この要件は、Dictionary
のキー 型に対する型制約によって強制されます。これは、キー 型が Swift 標準ライブラリで定義されている特別なプロトコルである Hashable
プロトコルに準拠する必要があることを指定します。 Swift のすべての基本型(String
、Int
、Double
、Bool
など)はデフォルトでハッシュ可能です。 独自のカスタム 型をHashable
プロトコルに準拠させる方法については、「ハッシュ可能プロトコルへの準拠(Link:developer.apple.com)(英語)」を参照してください。
You can define your own type constraints when creating custom generic types, and these constraints provide much of the power of generic programming. Abstract concepts like Hashable
characterize types in terms of their conceptual characteristics, rather than their concrete type.
カスタム ジェネリック型を作成するときに独自の型制約を定義でき、これらの制約によってジェネリック プログラミングの機能の多くが提供されます。 Hashable
のような抽象的な概念は、具体的な型ではなく、概念的な特性の観点から型を特徴づけます。
Type Constraint Syntax
型制約の構文
You write type constraints by placing a single class or protocol constraint after a type parameter’s name, separated by a colon, as part of the type parameter list. The basic syntax for type constraints on a generic function is shown below (although the syntax is the same for generic types):
型制約を記述するには、型パラメーターの名前の後に単一のクラス制約またはプロトコル制約を型パラメーター リストの一部としてコロンで区切って配置します。 ジェネリック関数の型制約の基本構文を以下に示します (ただし、構文はジェネリック型でも同じです)。
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// function body goes here
}
The hypothetical function above has two type parameters. The first type parameter, T
, has a type constraint that requires T
to be a subclass of SomeClass
. The second type parameter, U
, has a type constraint that requires U
to conform to the protocol SomeProtocol
.
上記の仮説関数には 2 つの型パラメーターがあります。 最初の型パラメータ T
には、T
が SomeClass
のサブクラスであることを要求する型制約があります。 2 番目の型パラメータ U
には、U
がプロトコル SomeProtocol
に準拠することを要求する型制約があります。
Type Constraints in Action
動作中の型制約
Here’s a nongeneric function called findIndex(ofString:in:)
, which is given a String
value to find and an array of String
values within which to find it. The findIndex(ofString:in:)
function returns an optional Int
value, which will be the index of the first matching string in the array if it’s found, or nil
if the string can’t be found:
これは、findIndex(ofString:in:)
という非ジェネリック関数です。この関数には、検索するString
値と、検索対象となるString
値の配列が与えられます。 findIndex(ofString:in:)
関数はオプションの Int
値を返します。これは、配列内で最初に一致する文字列が見つかった場合はそのインデックスとなり、文字列が見つからない場合は nil
を返します。
func findIndex(ofString valueToFind: String, in array: [String]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
The findIndex(ofString:in:)
function can be used to find a string value in an array of strings:
findIndex(ofString:in:)
関数を使用すると、文字列の配列内の文字列値を検索できます。
let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
if let foundIndex = findIndex(ofString: "llama", in: strings) {
print("The index of llama is \(foundIndex)")
}
// Prints "The index of llama is 2"
The principle of finding the index of a value in an array isn’t useful only for strings, however. You can write the same functionality as a generic function by replacing any mention of strings with values of some type T
instead.
ただし、配列内の値のインデックスを見つける原則は、文字列に対してのみ役立つわけではありません。 文字列への言及を代わりに何らかの型 T
の値に置き換えることで、同じ機能を汎用関数として記述することができます。
Here’s how you might expect a generic version of findIndex(ofString:in:)
, called findIndex(of:in:)
, to be written. Note that the return type of this function is still Int?
, because the function returns an optional index number, not an optional value from the array. Be warned, though — this function doesn’t compile, for reasons explained after the example:
ここでは、findIndex(of:in:)
と呼ばれる、findIndex(ofString:in:)
の汎用バージョンの記述方法を示します。 この関数は配列のオプションの値ではなく、オプションのインデックス番号を返すため、この関数の戻り値の型は依然として Int?
であることに注意してください。 ただし、警告します。この関数は、例の後で説明する理由によりコンパイルされません。
func findIndex<T>(of valueToFind: T, in array:[T]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
This function doesn’t compile as written above. The problem lies with the equality check, “if value == valueToFind
”. Not every type in Swift can be compared with the equal to operator (==
). If you create your own class or structure to represent a complex data model, for example, then the meaning of “equal to” for that class or structure isn’t something that Swift can guess for you. Because of this, it isn’t possible to guarantee that this code will work for every possible type T
, and an appropriate error is reported when you try to compile the code.
この関数は上記のようにコンパイルされません。 問題は、等価性チェック「if value == valueToFind
」にあります。 Swift のすべての型を等価演算子 (==
) で比較できるわけではありません。 たとえば、複雑なデータ モデルを表すために独自のクラスまたは構造体を作成する場合、そのクラスまたは構造体の「等しい」の意味は、Swift が推測できるものではありません。 このため、このコードが考えられるすべての型 T
で機能することを保証することはできず、コードをコンパイルしようとすると、適切なエラーが報告されます。
All is not lost, however. The Swift standard library defines a protocol called Equatable
, which requires any conforming type to implement the equal to operator (==
) and the not equal to operator (!=
) to compare any two values of that type. All of Swift’s standard types automatically support the Equatable
protocol.
ただし、すべてが失われたわけではありません。 Swift 標準ライブラリは、Equatable
と呼ばれるプロトコルを定義します。このプロトコルでは、その型の 2 つの値を比較するために、一致する型に等号演算子 (==
) と不等号演算子 (!=
) を実装する必要があります。 Swift のすべての標準型は、Equatable
プロトコルを自動的にサポートします。
Any type that’s Equatable
can be used safely with the findIndex(of:in:)
function, because it’s guaranteed to support the equal to operator. To express this fact, you write a type constraint of Equatable
as part of the type parameter’s definition when you define the function:
Equatable
の型は、等号演算子のサポートが保証されているため、findIndex(of:in:)
関数で安全に使用できます。 この事実を表現するには、関数を定義するときに型パラメータの定義の一部として Equatable
の型制約を記述します。
func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
The single type parameter for findIndex(of:in:)
is written as T: Equatable
, which means “any type T
that conforms to the Equatable
protocol.”
findIndex(of:in:)
の単一の型パラメータは、T: Equatable
と書かれます。これは、「Equatable
プロトコルに準拠する任意の型 T
」を意味します。
The findIndex(of:in:)
function now compiles successfully and can be used with any type that’s Equatable
, such as Double
or String
:
findIndex(of:in:)
関数は正常にコンパイルされ、Double
や String
などの Equatable
型で使用できるようになりました。
let doubleIndex = findIndex(of: 9.3, in: [3.14159, 0.1, 0.25])
// doubleIndex is an optional Int with no value, because 9.3 isn't in the array
let stringIndex = findIndex(of: "Andrea", in: ["Mike", "Malcolm", "Andrea"])
// stringIndex is an optional Int containing a value of 2
Associated Types
関連する型
When defining a protocol, it’s sometimes useful to declare one or more associated types as part of the protocol’s definition. An associated type gives a placeholder name to a type that’s used as part of the protocol. The actual type to use for that associated type isn’t specified until the protocol is adopted. Associated types are specified with the associatedtype
keyword.
プロトコルを定義するとき、プロトコルの定義の一部として 1 つ以上の関連する型を宣言すると便利な場合があります。 関連型は、プロトコルの一部として使用される型にプレースホルダ名を与えます。 関連付けられた型に使用する実際の型は、プロトコルが採用されるまで指定されません。 関連する型は、associatedtype
キーワードで指定されます。
Associated Types in Action
関連する型の動作
Here’s an example of a protocol called Container
, which declares an associated type called Item
:
以下は、Item
と呼ばれる関連付けられた型を宣言する Container
と呼ばれるプロトコルの例です。
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}
The Container
protocol defines three required capabilities that any container must provide:
Container
プロトコルは、コンテナが提供する必要がある 3 つの必須機能を定義します。
- It must be possible to add a new item to the container with an
append(_:)
method. append(_:)
メソッドを使用してコンテナに新しい項目を追加できる必要があります。- It must be possible to access a count of the items in the container through a
count
property that returns anInt
value. Int
値を返すcount
プロパティを通じて、コンテナ内のアイテムの数にアクセスできなければなりません。- It must be possible to retrieve each item in the container with a subscript that takes an
Int
index value. Int
インデックス値を取る添字を使用して、コンテナ内の各アイテムを取得できなければなりません。
This protocol doesn’t specify how the items in the container should be stored or what type they’re allowed to be. The protocol only specifies the three bits of functionality that any type must provide in order to be considered a Container
. A conforming type can provide additional functionality, as long as it satisfies these three requirements.
このプロトコルでは、コンテナ内のアイテムをどのように保存するか、またはどのような型が許可されるかは指定されていません。 このプロトコルでは、Container
とみなされるために任意の型が提供する必要がある 3 ビットの機能のみを指定します。 適合型は、これら 3 つの要件を満たす限り、追加の機能を提供できます。
Any type that conforms to the Container
protocol must be able to specify the type of values it stores. Specifically, it must ensure that only items of the right type are added to the container, and it must be clear about the type of the items returned by its subscript.
Container
プロトコルに準拠する型は、格納する値の型を指定できなければなりません。 具体的には、正しい型の項目のみがコンテナーに追加されるようにする必要があり、添え字によって返される項目の型を明確にする必要があります。
To define these requirements, the Container
protocol needs a way to refer to the type of the elements that a container will hold, without knowing what that type is for a specific container. The Container
protocol needs to specify that any value passed to the append(_:)
method must have the same type as the container’s element type, and that the value returned by the container’s subscript will be of the same type as the container’s element type.
これらの要件を定義するには、Container
プロトコルには、特定のコンテナの型が何であるかを知らなくても、コンテナが保持する要素の型を参照する方法が必要です。 Container
プロトコルでは、append(_:)
メソッドに渡される値はコンテナの要素型と同じ型である必要があり、コンテナの添え字によって返される値はコンテナの要素型と同じ型であることを指定する必要があります。
To achieve this, the Container
protocol declares an associated type called Item
, written as associatedtype Item
. The protocol doesn’t define what Item
is — that information is left for any conforming type to provide. Nonetheless, the Item
alias provides a way to refer to the type of the items in a Container
, and to define a type for use with the append(_:)
method and subscript, to ensure that the expected behavior of any Container
is enforced.
これを実現するために、Container
プロトコルは、associatedtype Item
.として記述される、Item
と呼ばれる関連付けられた型を宣言します。 プロトコルは、Item
が何であるかを定義しません。その情報は、適合する型が提供するために残されます。 それにもかかわらず、Item
エイリアスは、Container
内のアイテムの型を参照し、append(_:)
メソッドと添字で使用する型を定義して、Container
の期待される動作が確実に適用されるようにする方法を提供します。
Here’s a version of the nongeneric IntStack
type from Generic Types above, adapted to conform to the Container
protocol:
以下は、上記のジェネリック 型の非ジェネリック IntStack
型のバージョンで、コンテナ プロトコルに準拠するように調整されています。
struct IntStack: Container {
// original IntStack implementation
var items: [Int] = []
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
// conformance to the Container protocol
typealias Item = Int
mutating func append(_ item: Int) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}
The IntStack
type implements all three of the Container
protocol’s requirements, and in each case wraps part of the IntStack
type’s existing functionality to satisfy these requirements.
IntStack
型は、Container
プロトコルの 3 つの要件をすべて実装し、いずれの場合も、IntStack
型の既存の機能の一部をラップしてこれらの要件を満たすようにします。
Moreover, IntStack
specifies that for this implementation of Container
, the appropriate Item
to use is a type of Int
. The definition of typealias Item = Int
turns the abstract type of Item
into a concrete type of Int
for this implementation of the Container
protocol.
さらに、IntStack
は、このContainer
の実装に対して、使用する適切なItem
が Int
型であることを指定します。 typealias
のItem = Int
の定義は、Container
プロトコルのこの実装のために、Item
の抽象型をInt
の具象型に変えます。
Thanks to Swift’s type inference, you don’t actually need to declare a concrete Item
of Int
as part of the definition of IntStack
. Because IntStack
conforms to all of the requirements of the Container
protocol, Swift can infer the appropriate Item
to use, simply by looking at the type of the append(_:)
method’s item
parameter and the return type of the subscript. Indeed, if you delete the typealias Item = Int
line from the code above, everything still works, because it’s clear what type should be used for Item
.
Swift の型推論のおかげで、IntStack
の定義の一部として Int
の具体的な項目を実際に宣言する必要はありません。 IntStack
はContainer
プロトコルのすべての要件に準拠しているため、Swift は、append(_:)
メソッドの item
パラメータの型と添字の戻り値の型を確認するだけで、使用する適切な Item
を推測できます。 実際、上記のコードから typealias Item = Int
行を削除しても、Item
にどの型を使用する必要があるかが明確になるため、すべてが引き続き機能します。
You can also make the generic Stack
type conform to the Container
protocol:
汎用Stack
型をContainer
プロトコルに準拠させることもできます。
struct Stack<Element>: Container {
// original Stack<Element> implementation
var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
// conformance to the Container protocol
mutating func append(_ item: Element) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
This time, the type parameter Element
is used as the type of the append(_:)
method’s item
parameter and the return type of the subscript. Swift can therefore infer that Element
is the appropriate type to use as the Item
for this particular container.
今回は、型パラメータの Element
が、append(_:)
メソッドの item
パラメータの型と添字の戻り値の型として使用されます。 したがって、Swift は、Element
がこの特定のコンテナのItem
として使用するのに適切な型であると推測できます。
Extending an Existing Type to Specify an Associated Type
既存の型を拡張して関連する型を指定する
You can extend an existing type to add conformance to a protocol, as described in Adding Protocol Conformance with an Extension. This includes a protocol with an associated type.
「拡張機能によるプロトコル準拠の追加」で説明されているように、既存の型を拡張してプロトコルへの準拠を追加できます。 これには、関連付けられた型を持つプロトコルが含まれます。
Swift’s Array
type already provides an append(_:)
method, a count
property, and a subscript with an Int
index to retrieve its elements. These three capabilities match the requirements of the Container
protocol. This means that you can extend Array
to conform to the Container
protocol simply by declaring that Array
adopts the protocol. You do this with an empty extension, as described in Declaring Protocol Adoption with an Extension:
Swift の Array
型には、要素を取得するための append(_:)
メソッド、count
プロパティ、Int
インデックスを持つ添字がすでに提供されています。 これら 3 つの機能は、Container
プロトコルの要件に一致します。 これは、Array
がプロトコルを採用することを宣言するだけで、Array
を拡張してContainer
プロトコルに準拠できることを意味します。 これは、「拡張機能によるプロトコル採用の宣言」で説明されているように、空の拡張機能を使用して行います。
extension Array: Container {}
Array’s existing append(_:)
method and subscript enable Swift to infer the appropriate type to use for Item
, just as for the generic Stack
type above. After defining this extension, you can use any Array
as a Container
.
配列の既存の append(_:)
メソッドと添字により、上記の一般的なStack
型と同様に、Swift がItem
に使用する適切な型を推測できるようになります。 この拡張機能を定義すると、任意のArray
をContainer
として使用できるようになります。
Adding Constraints to an Associated Type
関連する型への制約の追加
You can add type constraints to an associated type in a protocol to require that conforming types satisfy those constraints. For example, the following code defines a version of Container
that requires the items in the container to be equatable.
プロトコル内の関連付けられた型に型制約を追加して、準拠する型がそれらの制約を満たすことを要求できます。 たとえば、次のコードは、コンテナ内のアイテムが同等であることを必要とするContainer
のバージョンを定義します。
protocol Container {
associatedtype Item: Equatable
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}
To conform to this version of Container
, the container’s Item
type has to conform to the Equatable
protocol.
このバージョンのContainer
に準拠するには、コンテナのItem
型が Equatable
プロトコルに準拠している必要があります。
Using a Protocol in Its Associated Type’s Constraints
関連する型の制約でのプロトコルの使用
A protocol can appear as part of its own requirements. For example, here’s a protocol that refines the Container
protocol, adding the requirement of a suffix(_:)
method. The suffix(_:)
method returns a given number of elements from the end of the container, storing them in an instance of the Suffix
type.
プロトコルは、それ自体の要件の一部として現れることがあります。 たとえば、これは、suffix(_:)
メソッドの要件を追加して、Container
プロトコルを改良したプロトコルです。 suffix(_:)
メソッドは、コンテナの末尾から指定された数の要素を返し、それらを Suffix
型のインスタンスに保存します。
protocol SuffixableContainer: Container {
associatedtype Suffix: SuffixableContainer where Suffix.Item == Item
func suffix(_ size: Int) -> Suffix
}
In this protocol, Suffix
is an associated type, like the Item
type in the Container
example above. Suffix
has two constraints: It must conform to the SuffixableContainer
protocol (the protocol currently being defined), and its Item
type must be the same as the container’s Item
type. The constraint on Item
is a generic where
clause, which is discussed in Associated Types with a Generic Where Clause below.
このプロトコルでは、Suffix
は、上記のContainer
の例のItem
型のように、関連付けられた型です。 Suffix
には 2 つの制約があります。1 つは SuffixableContainer
プロトコル (現在定義されているプロトコル) に準拠する必要があり、そのItem
型はコンテナのItem
型と同じである必要があります。 Item
の制約は一般的な Where
句であり、これについては、以下の汎用 Where 句との関連付けで説明します。
Here’s an extension of the Stack
type from Generic Types above that adds conformance to the SuffixableContainer
protocol:
以下は、SuffixableContainer
プロトコルへの準拠を追加する、上記のジェネリック 型のStack
型の拡張です。
extension Stack: SuffixableContainer {
func suffix(_ size: Int) -> Stack {
var result = Stack()
for index in (count-size)..<count {
result.append(self[index])
}
return result
}
// Inferred that Suffix is Stack.
}
var stackOfInts = Stack<Int>()
stackOfInts.append(10)
stackOfInts.append(20)
stackOfInts.append(30)
let suffix = stackOfInts.suffix(2)
// suffix contains 20 and 30
In the example above, the Suffix
associated type for Stack
is also Stack
, so the suffix operation on Stack
returns another Stack
. Alternatively, a type that conforms to SuffixableContainer
can have a Suffix
type that’s different from itself — meaning the suffix operation can return a different type. For example, here’s an extension to the nongeneric IntStack
type that adds SuffixableContainer
conformance, using Stack<Int>
as its suffix type instead of IntStack
:
上の例では、Stack
のSuffix
関連型もStack
なので、Stack
に対するサフィックス操作は別のStack
を返します。 あるいは、SuffixableContainer
に準拠する型は、それ自体とは異なる Suffix
型を持つことができます。つまり、サフィックスオペレーションは異なる型を返すことができます。 たとえば、これは、IntStack
の代わりに Stack<Int>
をサフィックス 型として使用して、SuffixableContainer
準拠を追加する非ジェネリック IntStack
型の拡張です。
extension IntStack: SuffixableContainer {
func suffix(_ size: Int) -> Stack<Int> {
var result = Stack<Int>()
for index in (count-size)..<count {
result.append(self[index])
}
return result
}
// Inferred that Suffix is Stack<Int>.
}
Generic Where Clauses
汎用 Where 句
Type constraints, as described in Type Constraints, enable you to define requirements on the type parameters associated with a generic function, subscript, or type.
「型の制約」で説明されているように、型の制約を使用すると、ジェネリック関数、添え字、または型に関連付けられた型パラメーターの要件を定義できます。
It can also be useful to define requirements for associated types. You do this by defining a generic where clause. A generic where
clause enables you to require that an associated type must conform to a certain protocol, or that certain type parameters and associated types must be the same. A generic where
clause starts with the where
keyword, followed by constraints for associated types or equality relationships between types and associated types. You write a generic where
clause right before the opening curly brace of a type or function’s body.
関連する型の要件を定義することも役立つ場合があります。 これを行うには、汎用 where 句を定義します。 汎用 where
句を使用すると、関連付けられた型が特定のプロトコルに準拠する必要があること、または特定の型パラメータと関連付けられた型が同じである必要があることを要求できます。 汎用 where
句は where
キーワードで始まり、その後に関連する型の制約、または型と関連する型間の等価関係が続きます。汎用 where
句は、型または関数の本体の左中括弧の直前に記述します。
The example below defines a generic function called allItemsMatch
, which checks to see if two Container
instances contain the same items in the same order. The function returns a Boolean value of true
if all items match and a value of false
if they don’t.
以下の例では、allItemsMatch
という汎用関数を定義しています。この関数は、2 つのContainer
インスタンスに同じアイテムが同じ順序で含まれているかどうかを確認します。 この関数は、すべての項目が一致する場合はブール値 true
を返し、一致しない場合は false
を返します。
The two containers to be checked don’t have to be the same type of container (although they can be), but they do have to hold the same type of items. This requirement is expressed through a combination of type constraints and a generic where
clause:
チェックする 2 つのコンテナは、同じ型のコンテナである必要はありません (同じである場合もあります)。ただし、同じ型のアイテムを保持する必要があります。 この要件は、型制約と一般的な where
句の組み合わせによって表現されます。
func allItemsMatch<C1: Container, C2: Container>
(_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.Item == C2.Item, C1.Item: Equatable {
// 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're equivalent.
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
// All items match, so return true.
return true
}
This function takes two arguments called someContainer
and anotherContainer
. The someContainer
argument is of type C1
, and the anotherContainer
argument is of type C2
. Both C1
and C2
are type parameters for two container types to be determined when the function is called.
この関数は、someContainer
と anotherContainer
という 2 つの引数を受け取ります。 someContainer
引数は C1
型で、anotherContainer
引数は C2
型です。 C1
と C2
はどちらも、関数の呼び出し時に決定される 2 つのコンテナ 型の型パラメータです。
The following requirements are placed on the function’s two type parameters:
関数の 2 つの型パラメーターには次の要件があります。
C1
must conform to theContainer
protocol (written asC1: Container
).C1
は、Container
プロトコル(C1:
Container
と表記)に準拠する必要があります。C2
must also conform to theContainer
protocol (written asC2: Container
).C2
も、Container
プロトコル(C2:
と表記)に準拠する必要があります。Container
- The
Item
forC1
must be the same as theItem
forC2
(written asC1.Item == C2.Item
). C1
のItem
は、C2
のItem
と同じである必要があります(C1.Item == C2.Item
として記述されます)。- The
Item
forC1
must conform to theEquatable
protocol (written asC1.Item: Equatable
). C1
のItem
は、Equatable
プロトコル(C1.Item: Equatable
と表記)に準拠する必要があります。
The first and second requirements are defined in the function’s type parameter list, and the third and fourth requirements are defined in the function’s generic where
clause.
1 番目と 2 番目の要件は関数の型パラメータ リストで定義され、3 番目と 4 番目の要件は関数の汎用 where
句で定義されます。
These requirements mean:
これらの要件は次のことを意味します。
someContainer
is a container of typeC1
.someContainer
は型C1
のコンテナです。anotherContainer
is a container of typeC2
.anotherContainer
は、型C2
のコンテナです。someContainer
andanotherContainer
contain the same type of items.someContainer
とanotherContainer
には同じ型のアイテムが含まれています。- The items in
someContainer
can be checked with the not equal operator (!=
) to see if they’re different from each other. someContainer
内の項目は、不等演算子 (!=
) を使用してチェックして、それらが互いに異なるかどうかを確認できます。
The third and fourth requirements combine to mean that the items in anotherContainer
can also be checked with the !=
operator, because they’re exactly the same type as the items in someContainer
.
3 番目と 4 番目の要件を組み合わせると、anotherContainer
内の項目も someContainer
内の項目とまったく同じ型であるため、!=
演算子でチェックできることになります。
These requirements enable the allItemsMatch(_:_:)
function to compare the two containers, even if they’re of a different container type.
これらの要件により、allItemsMatch(_:_:)
関数は、コンテナ 型が異なる場合でも 2 つのコンテナを比較できるようになります。
The allItemsMatch(_:_:)
function starts by checking that both containers contain the same number of items. If they contain a different number of items, there’s no way that they can match, and the function returns false
.
allItemsMatch(_:_:)
関数は、両方のコンテナに同じ数のアイテムが含まれていることを確認することから始まります。 含まれるアイテムの数が異なる場合、一致する方法はなく、関数は false
を返します。
After making this check, the function iterates over all of the items in someContainer
with a for
–in
loop and the half-open range operator (..<
). For each item, the function checks whether the item from someContainer
isn’t equal to the corresponding item in anotherContainer
. If the two items aren’t equal, then the two containers don’t match, and the function returns false
.
このチェックを行った後、関数は for-in
ループと半開範囲演算子 (..<
) を使用して someContainer
内のすべての項目を反復処理します。 この関数は、アイテムごとに、someContainer
のアイテムがanotherContainer
の対応するアイテムと等しくないかどうかをチェックします。 2 つの項目が等しくない場合、2 つのコンテナは一致せず、関数は false
を返します。
If the loop finishes without finding a mismatch, the two containers match, and the function returns true
.
不一致が見つからずにループが終了した場合、2 つのコンテナは一致し、関数は true
を返します。
Here’s how the allItemsMatch(_:_:)
function looks in action:
allItemsMatch(_:_:)
関数の動作は次のとおりです。
var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
var arrayOfStrings = ["uno", "dos", "tres"]
if allItemsMatch(stackOfStrings, arrayOfStrings) {
print("All items match.")
} else {
print("Not all items match.")
}
// Prints "All items match."
The example above creates a Stack
instance to store String
values, and pushes three strings onto the stack. The example also creates an Array
instance initialized with an array literal containing the same three strings as the stack. Even though the stack and the array are of a different type, they both conform to the Container
protocol, and both contain the same type of values. You can therefore call the allItemsMatch(_:_:)
function with these two containers as its arguments. In the example above, the allItemsMatch(_:_:)
function correctly reports that all of the items in the two containers match.
上記の例では、String
値を保存するStack
インスタンスを作成し、3 つの文字列をスタックにプッシュします。 この例では、スタックと同じ 3 つの文字列を含む配列リテラルで初期化された Array
インスタンスも作成します。 スタックと配列は型が異なりますが、どちらもContainer
プロトコルに準拠しており、同じ型の値が含まれています。 したがって、これら 2 つのコンテナを引数として allItemsMatch(_:_:)
関数を呼び出すことができます。 上の例では、allItemsMatch(_:_:)
関数は、2 つのコンテナ内のすべてのアイテムが一致することを正しく報告しています。
Extensions with a Generic Where Clause
汎用 Where 句を使用した拡張
You can also use a generic where
clause as part of an extension. The example below extends the generic Stack
structure from the previous examples to add an isTop(_:)
method.
拡張機能の一部として一般的な where
句を使用することもできます。 以下の例では、前の例の一般的なStack
構造を拡張して、isTop(_:)
メソッドを追加しています。
extension Stack where Element: Equatable {
func isTop(_ item: Element) -> Bool {
guard let topItem = items.last else {
return false
}
return topItem == item
}
}
This new isTop(_:)
method first checks that the stack isn’t empty, and then compares the given item against the stack’s topmost item. If you tried to do this without a generic where
clause, you would have a problem: The implementation of isTop(_:)
uses the ==
operator, but the definition of Stack
doesn’t require its items to be equatable, so using the ==
operator results in a compile-time error. Using a generic where
clause lets you add a new requirement to the extension, so that the extension adds the isTop(_:)
method only when the items in the stack are equatable.
この新しい isTop(_:)
メソッドは、最初にスタックが空でないことを確認し、次に指定された項目をスタックの最上位の項目と比較します。 汎用 where
句を使用せずにこれを実行しようとすると、問題が発生します。isTop(_:)
の実装では ==
演算子が使用されますが、Stack
の定義ではその項目が等価である必要はないため、 ==
演算子を使用するとコンパイル時エラーが発生します。 汎用 where
句を使用すると、拡張機能に新しい要件を追加できるため、スタック内の項目が等しい場合にのみ拡張機能が isTop(_:)
メソッドを追加します。
Here’s how the isTop(_:)
method looks in action:
isTop(_:)
メソッドが実際にどのように動作するかを次に示します。
if stackOfStrings.isTop("tres") {
print("Top element is tres.")
} else {
print("Top element is something else.")
}
// Prints "Top element is tres."
If you try to call the isTop(_:)
method on a stack whose elements aren’t equatable, you’ll get a compile-time error.
要素が等価でないスタックで isTop(_:)
メソッドを呼び出そうとすると、コンパイル時エラーが発生します。
struct NotEquatable { }
var notEquatableStack = Stack<NotEquatable>()
let notEquatableValue = NotEquatable()
notEquatableStack.push(notEquatableValue)
notEquatableStack.isTop(notEquatableValue) // Error
You can use a generic where
clause with extensions to a protocol. The example below extends the Container
protocol from the previous examples to add a startsWith(_:)
method.
プロトコルの拡張機能を備えた一般的な where
句を使用できます。 以下の例では、前の例のContainer
プロトコルを拡張して、startsWith(_:)
メソッドを追加しています。
extension Container where Item: Equatable {
func startsWith(_ item: Item) -> Bool {
return count >= 1 && self[0] == item
}
}
The startsWith(_:)
method first makes sure that the container has at least one item, and then it checks whether the first item in the container matches the given item. This new startsWith(_:)
method can be used with any type that conforms to the Container
protocol, including the stacks and arrays used above, as long as the container’s items are equatable.
startsWith(_:)
メソッドは、まずコンテナに少なくとも 1 つのアイテムがあることを確認し、次にコンテナ内の最初のアイテムが指定されたアイテムと一致するかどうかを確認します。 この新しい startsWith(_:)
メソッドは、コンテナ のアイテムが同等である限り、上で使用したスタックや配列など、Container
プロトコルに準拠する任意の型で使用できます。
if [9, 9, 9].startsWith(42) {
print("Starts with 42.")
} else {
print("Starts with something else.")
}
// Prints "Starts with something else."
The generic where
clause in the example above requires Item
to conform to a protocol, but you can also write a generic where
clauses that require Item
to be a specific type. For example:
上の例の汎用where
句では、Item
がプロトコルに準拠する必要がありますが、Item
が特定の型であることを必要とする汎用 where
句を記述することもできます。 例えば:
extension Container where Item == Double {
func average() -> Double {
var sum = 0.0
for index in 0..<count {
sum += self[index]
}
return sum / Double(count)
}
}
print([1260.0, 1200.0, 98.6, 37.0].average())
// Prints "648.9"
This example adds an average()
method to containers whose Item
type is Double
. It iterates over the items in the container to add them up, and divides by the container’s count to compute the average. It explicitly converts the count from Int
to Double
to be able to do floating-point division.
この例では、Item
型が Double
であるコンテナにaverage()
メソッドを追加します。 コンテナ内のアイテムを反復して合計し、コンテナの数で割って平均を計算します。 浮動小数点除算を実行できるように、カウントを Int
から Double
に明示的に変換します。
You can include multiple requirements in a generic where
clause that’s part of an extension, just like you can for a generic where
clause that you write elsewhere. Separate each requirement in the list with a comma.
他の場所に記述する一般的な where
句と同様に、拡張機能の一部である一般的な where
句に複数の要件を含めることができます。 リスト内の各要件はカンマで区切ります。
Contextual Where Clauses
コンテキストに応じた Where 句
You can write a generic where
clause as part of a declaration that doesn’t have its own generic type constraints, when you’re already working in the context of generic types. For example, you can write a generic where
clause on a subscript of a generic type or on a method in an extension to a generic type. The Container
structure is generic, and the where
clauses in the example below specify what type constraints have to be satisfied to make these new methods available on a container.
すでにジェネリック型のコンテキストで作業している場合は、独自のジェネリック型制約を持たない宣言の一部としてジェネリックの where
句を記述することができます。 たとえば、ジェネリック型の添字またはジェネリック型の拡張内のメソッドにジェネリックな where
句を記述することができます。 Container
の構造は汎用的なもので、以下の例の where
句は、これらの新しいメソッドをコンテナで使用できるようにするために満たす必要がある型の制約を指定します。
extension Container {
func average() -> Double where Item == Int {
var sum = 0.0
for index in 0..<count {
sum += Double(self[index])
}
return sum / Double(count)
}
func endsWith(_ item: Item) -> Bool where Item: Equatable {
return count >= 1 && self[count-1] == item
}
}
let numbers = [1260, 1200, 98, 37]
print(numbers.average())
// Prints "648.75"
print(numbers.endsWith(37))
// Prints "true"
This example adds an average()
method to Container
when the items are integers, and it adds an endsWith(_:)
method when the items are equatable. Both functions include a generic where
clause that adds type constraints to the generic Item
type parameter from the original declaration of Container
.
この例では、アイテムが整数の場合は、average()
メソッドをContainer
に追加し、アイテムが等しい場合は、endsWith(_:)
メソッドを追加します。 どちらの関数にも、Container
の元の宣言からの汎用Item
型パラメータに型制約を追加する汎用 where
句が含まれています。
If you want to write this code without using contextual where
clauses, you write two extensions, one for each generic where
clause. The example above and the example below have the same behavior.
コンテキストに応じた where
句を使用せずにこのコードを作成する場合は、一般的な where
句ごとに 1 つずつ、合計 2 つの拡張機能を作成します。 上の例と下の例は同じ動作をします。
extension Container where Item == Int {
func average() -> Double {
var sum = 0.0
for index in 0..<count {
sum += Double(self[index])
}
return sum / Double(count)
}
}
extension Container where Item: Equatable {
func endsWith(_ item: Item) -> Bool {
return count >= 1 && self[count-1] == item
}
}
In the version of this example that uses contextual where
clauses, the implementation of average()
and endsWith(_:)
are both in the same extension because each method’s generic where
clause states the requirements that need to be satisfied to make that method available. Moving those requirements to the extensions’ generic where
clauses makes the methods available in the same situations, but requires one extension per requirement.
コンテキストに応じた where
句を使用するこの例のバージョンでは、average()
と endsWith(_:)
の実装は両方とも同じ拡張機能内にあります。これは、各メソッドの汎用 where
句が、そのメソッドを使用可能にするために満たす必要がある要件を示しているためです。 これらの要件を拡張機能の一般的な where
句に移動すると、同じ状況でメソッドが使用できるようになりますが、要件ごとに 1 つの拡張機能が必要になります。
Associated Types with a Generic Where Clause
汎用 Where 句を使用した関連付けられた型
You can include a generic where
clause on an associated type. For example, suppose you want to make a version of Container
that includes an iterator, like what the Sequence
protocol uses in the Swift standard library. Here’s how you write that:
関連する型に一般的な where
句を含めることができます。 たとえば、Swift 標準ライブラリの Sequence
プロトコルで使用されるようなイテレータを含むバージョンの Container
を作成したいとします。 書き方は次のとおりです。
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
associatedtype Iterator: IteratorProtocol where Iterator.Element == Item
func makeIterator() -> Iterator
}
The generic where
clause on Iterator
requires that the iterator must traverse over elements of the same item type as the container’s items, regardless of the iterator’s type. The makeIterator()
function provides access to a container’s iterator.
Iterator
の汎用 where
句では、イテレータの型に関係なく、イテレータがコンテナの項目と同じ項目型の要素である必要があります。 makeIterator()
関数は、コンテナのイテレータへのアクセスを提供します。
For a protocol that inherits from another protocol, you add a constraint to an inherited associated type by including the generic where
clause in the protocol declaration. For example, the following code declares a ComparableContainer
protocol that requires Item
to conform to Comparable
:
別のプロトコルを継承するプロトコルの場合、プロトコル宣言に汎用の where
句を含めることで、継承された関連型に制約を追加します。 たとえば、次のコードは、Item
が Comparable
に準拠することを要求する ComparableContainer
プロトコルを宣言します。
protocol ComparableContainer: Container where Item: Comparable { }
Generic Subscripts
汎用添え字
Subscripts can be generic, and they can include generic where
clauses. You write the placeholder type name inside angle brackets after subscript
, and you write a generic where
clause right before the opening curly brace of the subscript’s body. For example:
添え字は汎用的なものにすることができ、汎用的な where
句を含めることもできます。 プレースホルダの型名をsubscript
の後に山括弧内に書き、添え字の本文の左中括弧の直前に一般的な where
句を書きます。 例えば:
extension Container {
subscript<Indices: Sequence>(indices: Indices) -> [Item]
where Indices.Iterator.Element == Int {
var result: [Item] = []
for index in indices {
result.append(self[index])
}
return result
}
}
This extension to the Container
protocol adds a subscript that takes a sequence of indices and returns an array containing the items at each given index. This generic subscript is constrained as follows:
Container
プロトコルへのこの拡張機能は、一連のインデックスを取得し、指定された各インデックスの項目を含む配列を返す添字を追加します。 この汎用添字は次のように制約されます。
- The generic parameter
Indices
in angle brackets has to be a type that conforms to theSequence
protocol from the Swift standard library. - 山括弧内の汎用パラメータ
Indices
は、Swift 標準ライブラリのSequence
プロトコルに準拠する型である必要があります。 - The subscript takes a single parameter,
indices
, which is an instance of thatIndices
type. - 添え字は、その
Indices
型のインスタンスであるindices
という単一のパラメータを取ります。 - The generic
where
clause requires that the iterator for the sequence must traverse over elements of typeInt
. This ensures that the indices in the sequence are the same type as the indices used for a container. - 汎用
where
句では、シーケンスの反復子がInt
型の要素である必要があります。 これにより、シーケンス内のインデックスがコンテナーに使用されるインデックスと同じ型になることが保証されます。
Taken together, these constraints mean that the value passed for the indices
parameter is a sequence of integers.
これらの制約を総合すると、indices
パラメータに渡される値は一連の整数であることを意味します。