はじめに
Swiftでは、言語仕様のアップデートが日々プロポーサルという形で提案・議論されています。
Swift6.2では、便利なアップデートが多く実施されるようです。
今回は、Swift6.2で導入される予定のAdd Collection conformances for enumerated()
というプロポーサルについてまとめます。
プロポーサルのリンクはこちら
TL;DR
enumerated()
の返り値であるEnumeratedSequence
がCollection
に準拠するようになります
これによって以下の嬉しさがあります
- 特定の操作に対するパフォーマンスが大きく向上します
- SwiftUIの
List
やForEach
に対して、Arrayでラップせずに直接EnumeratedSequence
を渡せるようになります。
プロポーサルの概要
プロポーサルのIntroductionの章をみると、次のように書かれています。
This proposal aims to fix the lack of Collection conformance of the sequence returned by enumerated(), preventing it from being used in a context that requires a Collection.
日本語に訳すと以下のようになるでしょうか。
この提案は、enumerated() によって返されるシーケンスが Collection に準拠していないという問題を修正し、Collection を必要とするコンテキストで使用できるようにすることを目的としています。
つまり、enumerated()
が返すEmumeratedSequence
がCollection
に準拠するということのようです。
enumerated()とはなにか
まずは、今回の議論の対象であるenumerated()
について確認しましょう。
公式ドキュメントには以下のような説明がされています。
Returns a sequence of pairs (n, x), where n represents a consecutive integer starting at zero and x represents an element of the sequence.
つまり、Arrayに対して、({0からの連番}, {Arrayの要素})をもつEnumeratedSequence
を返すメゾットということのようですね。
簡単なコードで確認してみましょう。
let string = "Hello, World!"
for char in string.enumerated() {
print(char)
}
上のコードの実行結果は、以下のようになります
(offset: 0, element: "H")
(offset: 1, element: "e")
(offset: 2, element: "l")
(offset: 3, element: "l")
(offset: 4, element: "o")
(offset: 5, element: ",")
(offset: 6, element: " ")
(offset: 7, element: "W")
(offset: 8, element: "o")
(offset: 9, element: "r")
(offset: 10, element: "l")
(offset: 11, element: "d")
(offset: 12, element: "!")
0からの連番にはoffset
、Arrayの要素にはelement
というラベルが与えられるようですね
繰り返し処理でindexを利用したい場合には非常に便利そうです
enumerated()がCollectionに準拠するとどうなるのか
このenumerated()
がCollection
に準拠すると、どのような利点があるのでしょうか?
プロポーサルのMotivationの章には3点が紹介されていたので、順に説明します。
EnumeratedSequenceが特定の操作を高速に実行できるようになる
プロポーサルでは次のような記述で解説されています。
(1000..
EnumeratedSequence
がCollection
に準拠していない場合、dropFirst(500)
の実行時には、要素を1つずつ順にスキップする必要があり、O(n)の実行時間を要します。
Collection
に準拠している場合、dropFirst(500)
は内部的にindexが500の要素からのスライス処理によってO(1)の実行時間になります。
このようなポインタの移動や範囲指定によって達成できる操作のパフォーマンスが大きく向上されられるようです。
reversed()の返り値がReversedColectionになる
プロポーサルでは次のような記述で解説されています。
“abc”.enumerated().reversed() will return a ReversedCollection rather than allocating a new array.
Collection
への準拠のないEnumeratedSequence
に対してreverse()
を実行した場合、内部的にはすべての要素を走査してArrayに変換し、逆順に並べ直す必要があります。
Collection
への準拠によって、ReversedCollection
にラップして返すだけで、reverse()
の処理を完了でき、O(1)の実行時間で処理を完了できます。
中間生成物であるArrayも不要です。
こちらも、パフォーマンスの大きな向上が期待できそうですね。
SwiftUIのListやForEachでの利用がシンプルになる
プロポーサルでは次のような記述で解説されています。
SwiftUI’s List and ForEach views will be able to directly take an enumerated collection as their data.
個人的には、一番うれしい利点です。
SwiftUIでは、List
やForEach
に配列やコレクションを渡すことで要素を表示することができます。
しかし、EnumeratedSequence
がCollection
に準拠していないために、直接List
やForEachに
EnumeratedSequence`を直接渡すことができませんでした。
これを解決するためにArrayでラップするなどの工夫をしている方が多いのではないでしょうか。
例えば、以下のViewはコンパイルエラーになりません。
struct ContentView: View {
let strings = ["a", "b", "c"]
var body: some View {
VStack {
ForEach(strings, id: \.self) { string in
Text(string)
}
}
}
}
次のViewはSwift6.2未満ではコンパイルエラーになります
struct ContentView: View {
let strings = ["a", "b", "c"]
var body: some View {
VStack {
// strings.enumerated()はEnumeratedSequenceを返すが、Collectionに準拠していないのでコンパイル不可
ForEach(strings.enumerated(), id: \.offset) { string in
Text(string.element)
}
}
}
}
エラーコードが以下になっており、RandomAccessCollection
に準拠していないためにコンパイルできないことがわかります
Generic struct ‘ForEach’ requires that ‘EnumeratedSequence’ conform to ‘RandomAccessCollection’
これを回避するためには、Arrayでラップするなどの工夫が必要です。
struct ContentView: View {
let strings = ["a", "b", "c"]
var body: some View {
VStack {
// ArrayはCollectionに準拠しているのでコンパイルできる
ForEach(Array(strings.enumerated()), id: \.self) { string in
Text(string)
}
}
}
}
ArrayはCollection
に準拠しているため、EnumeratedSequence
をラップすることで問題なくコンパイル可能です。
EnumeratedSequence
がCollection
に準拠することで、Arrayでラップする必要がなくなり、より直感的な記述ができるようになります。
まとめ
今回は、Swift6.2で導入予定のAdd Collection conformances for enumerated()
というプロポーサルについてまとめてみました。
enumerated()
の返り値であるEnumeratedSequence
がCollection
に準拠することで
- これまで各要素を走査する必要があった処理の一部が、ポインタの移動や範囲指定によって効率的に実行できるようになる
- ReversedCollectionを利用することで、逆順の生成が効率的になる
- SwiftUIのListやForEachで、より直感的な記述ができるようになる
といった利点があることがわかりました!
今後indexを利用したViewの繰り返し処理でお世話になりそうな、ちょっと嬉しい変更ですね!
好評であればいくつかSwift6.2で採用されるプロポーサルをまとめようと思います。
ぜひいいねやストックなどよろしくお願いします!
Views: 0