🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
関数型プログラミング

関数型プログラミングとは、関数を用いて演算子を構築し、算出し、コンピュータプログラムを構成する枠組みです。

Swift

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

Q&A

解決済

1回答

550閲覧

Arrayを引数に持つ関数の要素に対する型指定の方法

AK-10

総合スコア8

関数型プログラミング

関数型プログラミングとは、関数を用いて演算子を構築し、算出し、コンピュータプログラムを構成する枠組みです。

Swift

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

0グッド

0クリップ

投稿2019/11/16 20:36

編集2019/11/16 22:17

前提

Arrayの連結を行う++演算子を定義しています.

swift

1 2infix operator ++ : AdditionPrecedence 3 4extension Array { 5 static func ++(lhs: Array, rhs: Array) -> Array { 6 var result = lhs 7 result.append(contentsOf: rhs) 8 return result 9 } 10}

発生している問題

連結自体はできるのですが, lhsとrhsの型が違う場合, Array<Any>型の値を返してしまいます.

swift

1print(type(of: ([1,2,3] ++ [4]))) // Array<Int> 2print(type(of: ([1,2,3] ++ ["str"]))) // Array<Any>

実現したいこと

Haskellの++演算子のように要素の型が違う場合,エラーを発生させたいです.

ghciでの実行結果は以下のようになります

haskell

1:t [1,2,3] ++ [4] 2[1,2,3] ++ [4] :: Num a => [a] 3 4:t [1,2,3] ++ ["str"] -- error 5-- No instance for (Num [Char]) arising from the literal ‘1’ 6

試したこと

Swiftの+演算子は前提で書いた実装と似たような挙動をしていました.

swift

1print(type(of: ([1,2,3] + [4]))) // IndexPath 2print(type(of: ([1,2,3] + ["str"]))) // Array<Any>

そもそも要素の型指定はできないからこうなっているのか, 意図してこういう挙動にしているのかわかりませんでした.
そこもできれば教えていただきたいです.

またダメ元でGenericsを使ってやってみましたが,演算子に型変数をつけることができなかったので,ダメです.

補足情報(FW/ツールのバージョンなど)

OS: macOS 10.14.6
Apple Swift version 5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7)
Target: x86_64-apple-darwin18.7.0

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

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

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

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

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

guest

回答1

0

ベストアンサー

SwiftだとなんでもAnyにキャスト出来てしまうので普通のやり方だと無理ですね。
面倒くさいですけどこうするとご希望のものになります。

swift

1infix operator ++ : AdditionPrecedence 2 3protocol P {} 4 5extension Array where Element: P { 6 static func ++ (lhs: Array, rhs: Array) -> Array { 7 lhs + rhs 8 } 9} 10 11extension Int: P {}

要素をprotocol Pに限定します。
AnyはPではなく、extensionも生やせないのでAnyにキャストされることはありません。
ただし ++ を使いたい型全てをPに準拠させる必要があります。

投稿2019/11/17 12:29

MasakiHori

総合スコア3391

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

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

AK-10

2019/11/17 13:55

ご回答ありがとうございます! 確かに,希望した動作になっていますね! ただ,私の直感と少し反している部分があるので追加で質問させてください. 回答にあるコードをみて初めに思ったのは, protocol Pを準拠している別の型を用意した場合(次のような場合) ```swift protocol P {} extension Int: P {} extension String: P {} ``` 結局Anyの時と変わらないように見えました.(ElementがPに準拠したものであれば引数に代入することを許すと思っていた.) 実際のところ,私の予想とは違い,IntのPとStringのPが別物として扱われているような挙動をしています. これはswiftの推論規則がこのように解釈するからという認識であってますでしょうか?
MasakiHori

2019/11/17 14:20 編集

Arrayは本来型パラメータを持っていますが、extensionではそれを省略して書くことが出来ます。 このときArrayとだけ記されたものはすべてArray<Element>と解釈されます。 省略せずに書けば static func ++ (lhs: Array<Element>, rhs: Array<Element>) -> Array<Element> となります。 つまり左右両項の要素の型は同一でなければならないのです。(戻り値も同じです) ---- 今気付きましたが、こう書く方がよりSwiftらしいかも。 static func ++ (lhs: Self, rhs: Self) -> Self
AK-10

2019/11/17 14:39 編集

私もそのように認識しています. ここではElementをPにしているので 省略せずに書くと static func ++(lhs: Array<P>, rhs: Array<P>) -> Array<P> となります. lhsとrhsの要素の型はprotocol P を準拠しているかどうかのみに依存していると思っていたのです. そういう意味でlhs: (Int: P), rhs: (String: P)であってもコンパイルは通りそうだなと思いました.
MasakiHori

2019/11/18 01:16

static func ++(lhs: Array<P>, rhs: Array<P>) -> Array<P> こうではないです。 あくまで要素はElementです。 そのうえで where Element: P によってElementがP準拠であることを条件としているのです。
AK-10

2019/11/18 01:25

あー,なるほど! 理解しました! Genericsで<T: Equatable> のように書くのと同じですね.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問