質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

376閲覧

Swift Array型の型引数Elementに型制約を課す方法について

moriman

総合スコア615

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

1クリップ

投稿2020/02/09 17:27

protocol Container { associatedtype Item mutating func append(_ item: Item) var count: Int { get } subscript(i: Int) -> Item { get } } extension Array: 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] } } 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 } 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.") } //All items match.

https://docs.swift.org/swift-book/LanguageGuide/Generics.html
上記ページのGeneric Where Clausesの部分のサンプルコードを見ていて頭に浮かんだただの興味なのですが質問させてください。このコードの動き自体はなんとか
理解できました。Containerプロトコルの定義内の連想型Itemには特に型制約は無く、
allItemsMatch(::)関数のGeneric Where Clausesの

C1.Item: Equatable

によりcontainerの要素の型を「Equatableプロトコルに準拠した型」と指定しています。
まあこれで動くのですが、どうせcontainerの要素の型を「Equatableプロトコルに準拠した型」に
指定するのなら、Containerプロトコルの定義内の連想型Itemを

protocol Container { associatedtype Item:Equatable ...

のように指定した方が、自然な気がしました。
そこでallItemsMatch(::)関数のGeneric Where Clause部分の

C1.Item: Equatable

を消して、代わりにContainerプロトコルの定義内の連想型Itemを

protocol Container { associatedtype Item:Equatable ...

のようにしても問題無く動くのではないか、と思いやってみたのですが(以下のコード)、

protocol Container { associatedtype Item:Equatable mutating func append(_ item: Item) var count: Int { get } subscript(i: Int) -> Item { get } } extension Array: 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] } } func allItemsMatch<C1: Container, C2: Container> (_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.Item == C2.Item{ // 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 } 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.") }
error: type 'Stack<Element>' does not conform to protocol 'Container' error: type 'Array<Element>' does not conform to protocol 'Container'

上記のようなエラーが出ました。つまりContainerプロトコルを変えたことにより、Array型、Stack<Element>
がContainerプロトコルに準拠できなくなった、ということらしいのですが、これを解決するためにはArray型、Stack<Element>型の定義内で、連想型Itemを「Equatableプロトコルに準拠した型」に指定する必要がある、
ということになるのでしょうか。ただArray型もStack<Element>型もジェネリック型なので、連想型Itemと紐付く型は型引数のElementになると思います。ですので、型引数Elementに対して型制約(Equatableプロトコルに準拠しなければならない)を課す必要がある、ということになるのでしょうか。

ここでStack<Element>型は、Stack<Element>の定義内で、

struct Stack<Element:Equatable>: Container { // original Stack<Element> implementation ....

のようにすればエラーは消えました。結局Stack<Element>型は(組み込み型では無く)独自の型なので、このような型制約の指定も、開発者が好きなようにすればいいような気がしますが、Array型は組み込み型なので、その定義をいじるのは、普通しないような気がします。(いじるとしてもいじり方もよくわからないし、それよりもいじって他で問題が出ないか、そちらの方が問題が大きいような気がする。ただこのコードに関して言えば、Stack<Element>もArray<Element>も似たようなものなので、ArrayElement:Equatableとすれば良いだけで、特に問題は無いような気もしますがよくわかりません。)
ということで、Array型に関しては定義はそのままで、例えばエクステンションなりでエラーを出なくする方法はあるでしょうか?
エクステンションなりでエラーを消そうとするのも普通はやらないのかもしれませんが、あくまで理論的な話として何か方法があるのか、気になったので質問させていただきます。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

以下のように型制約を指定できます。

swift

1extension Array: Container where Element: Equatable {}

追記

swift

1extension Array where Element:Equatable{}

はArrayのElementをEquatableに準拠させるという意味ではありません。

これは、ArrayのうちElementがEquatableであるもののみに拡張を与えるという宣言です。

例えば

swift

1extension Array where Element: Equatable { 2 func allSame() -> Bool { 3 guard let e = first else { return true } 4 return reduce(true) { $0 ? $1 == e : false } // $1 == e はElementがEquatableでなければ実行不可能 5 } 6}

のようなElementがEquatableでなければ実行不可能なメソッドの追加などを行う時に利用します。

僕が回答したextensionは
ArrayのうちElementがEquatableであるもののみをContainerに準拠させる。
という意味になります。

投稿2020/02/10 00:36

編集2020/02/10 03:20
MasakiHori

総合スコア3391

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

moriman

2020/02/10 02:07 編集

回答をいただきましてありがとうございます。 頂いたコードでエラーが発生しなくなりました。 これで解決なのですが、もう一つ、質問投稿する前に、ドキュメントの見様見真似で、頂いたコード を書くべき部分に extension Array where Element:Equatable{} extension Array: Container {} と書いて動かした結果(他の部分は全て同じ) error: MyPlayground.playground:561:4: error: generic parameter 'C1' could not be inferred if allItemsMatch(stackOfStrings, arrayOfStrings) { ^ MyPlayground.playground:533:6: note: in call to function 'allItemsMatch' func allItemsMatch<C1: Container, C2: Container> ^ error: MyPlayground.playground:504:1: error: type 'Array<Element>' does not conform to protocol 'Container' extension Array: Container {} ^ MyPlayground.playground:497:20: note: unable to infer associated type 'Item' for protocol 'Container' associatedtype Item:Equatable ^ 上記のようなエラーが出て、よくわからないので質問させていただいた、という経緯なんです。 私の書いたコードではなぜエラーが出るのでしょうか。どこがダメなのでしょうか?
moriman

2020/02/10 04:38

つまり同じ配列(Array型インスタンス)でも格納している要素の型次第で、 「あるプロトコルに準拠している、とみなされるArray型インスタンス」 と 「あるプロトコルに準拠しているとみなされないArray型インスタンス」 が存在することになりますよね。 非常に柔軟ですよね。
moriman

2020/02/11 00:36

理解できました。サンプルコードも示して頂きましてありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問