趣味でIOSアプリ開発をかじっていた自分が、改めてSwiftを勉強し始めた際に、曖昧に理解していたところや知らなかったところをメモしています。いつか書き直します。
参考文献
この記事は以下の書籍の情報を参考にして執筆しました。
ジェネリクス
型をパラメータとしてプログラムに記述するための機能。
ジェネリクスを使った関数例
型パラメータの書き方
<T> : 関数定義、型定義に書くことによってTを型パラメータとして使うことを示す。
<T,U> : 型パラメータが2つ欲しいときはこのように書く。
<T: OtherType> : TはプロトコルOtherTypeに適合するか、クラスOtherType自体かそのサブクラスであることを示す。
where 条件 : T:OtherTypeやT==Uのような条件を記述する。
プロトコルによる制約
func mySwap<T: Comparable>(little a: inout T, great b: inout T){
if a < b {
let t = a; a = b; b = t
}
}
var a = "hoge"
var b = "piyo"
print(a,b) // hoge piyo
mySwap(little: &a, great: &b)
print(a,b) // piyo hoge
var c = 10
var d = 30
print(c,d) // 10 30
mySwap(little: &c, great: &d)
print(c,d) // 30 10
var e = ("hoge", 0)
var f = ("piyo", 100)
mySwap(little: &e, great: &f) // error Comparableに適応してないことを指摘される。
型パラメータの推論
// 任意のオプショナル型配列を返す関数
func f<T>(_ n:Int) -> [T?] {
return [T?]( repeating:nil, count:n )
}
//let a = f(3) // 型推論の座愛量がないのでエラーとなる
let a: [Int?] = f(3)
print(a) // [nil, nil, nil]
型パラメータをもつ構造体の定義
構造体や列挙型、クラスが型パラメータを持つように定義することができる。
定義方法は名前の後にをつける。
て戯号するべきプロトコルなどの条件をつけることもできる。
// プロトコルを作成
protocol SimpleVector {
associatedtype Element
var x : Element { get }
var y : Element { get }
}
// 型パラメータを持つ構造体を定義
struct Vector<T> : SimpleVector {
typealias Element = T
var x,y : T
}
型パラメータを持つtypealias
型パラメータに複雑な制約をつけることも可能。
typealias Avatar<T,U> = (power: T, armor: U) where T: Comparable, U:Collection, U.Element == (String, T)
let fighter: Avatar = (power: 90, armor: [("Sword", 50), ("Hoge", 10)])
print(fighter.armor.first!) // ("Sword", 50)
不透明型
関数の戻り値型としてSelfまたは付属生を持つプロトコルを指定する。
例えば下記の例で返り値にRankedというプロトコルを指定するが、プロパティに含まれるRank型が何かわからないのでエラーとなる。
このような場合にプロトコルの前にsomeというキーワードを置いて、返り値の型として使用できるようになる。
protocol Ranked {
associatedtype Rank: Comparable
var name: String { get }
var rank: Rank { get }
}
struct Person: Ranked {
var name: String
var rank: Int
}
//func comparRank<T>(_ a: T, _ b: T) -> Ranked where T: Ranked { // error
func comparRank<T>(_ a: T, _ b: T) -> some Ranked where T: Ranked {
let c = (a.rank > b.rank) ? a : b
return Person(name: c.name, rank: c.rank as! Int )
}
let a = Person(name:"hoge", rank: 1)
let b = Person(name:"fuga", rank: 30)
let c = comparRank(a, b)
print(c.name, c.rank)
不透明型の性質
下記のようなプログラムを実行するときsomeを使わずにプロトコルを返す関数を実装。
返り値を2通り返すような処理にしてsomeを記述するとエラーとなる。
someを指定した場合プロトコルに適合するいずれか1種類の型でないといけない。
protocol Named {
var name: String { get }
}
protocol Ranked : Named{
associatedtype Rank: Comparable
// var name: String { get }
var rank: Rank { get }
}
struct Person: Ranked {
var name: String
var rank: Int
}
//func comparRank<T>(_ a: T, _ b: T) -> some Named where T: Named { // error
func comparRank<T>(_ a: T, _ b: T) -> Named where T: Named {
if (a.name > b.name) {
return Person(name: a.name, rank: 0)
}
return b
}
let a = Person(name:"hoge", rank: 1)
let b = Person(name:"fuga", rank: 30)
let c = comparRank(a, b)
print(c.name)
//print(c.rank) // error Namedプロトコルにrankがないので
また、some Named型が複数存在する場合、それぞれが違う型とされるので代入などできない。
func newMember(name: String) -> some Named {
return Person(name: name, rank: 0)
}
var a: some Named = newMember(name: "hoge")
var b: some Named = newMember(name: "fuga")
a = b //error
クロージャやジェネリクスを使って提供されているメソッド
filter - 条件を満たす要素を取り出す
let a = (1...100).filter { $0 % 22 == 0 || $0 % 33 == 0 }
print(a) // [22, 33, 44, 66, 88, 99]
map - 各要素を処理した結果を配列として得る
let b = (1...10).map { $0 * 3}
print(b) // [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]
filterと組み合わせて使うこともできる
let c = (1...10).map { $0 * 3}.filter { $0 > 15}
print(c) // [18, 21, 24, 27, 30]
mapValues - 辞書の含まれる要素の値に対して処理を適用し、その結果とキーを組みあわせた辞書を返す。
let d = ["りんご":100, "みかん":80, "ぶどう":300]
let mapped = d.mapValues{ (value:Int) -> Int in
return Int(Double(value) * 1.1)
}
print(mapped) // ["りんご": 110, "ぶどう": 330, "みかん": 88]
compactMap - 引数クロージャがnilを返した場合配列に含めない
let e = ["Hoge", "fuga","1", "Pochi","piyo", "99"].map { Int($0) }
print(e) // [nil, nil, Optional(1), nil, nil, Optional(99)]
let f = ["Hoge", "fuga","1", "Pochi","piyo", "99"].compactMap { Int($0) }
print(f) // [1, 99]
flatMap - シーケンスを返すクロージャを引数とし、全てのクロージャの要素を含む配列を返す
let g = [["Hoge", "fuga"], ["dog", "Pochi"], ["piyo", "foo"]].flatMap {$0}
print(g) // ["Hoge", "fuga", "dog", "Pochi", "piyo", "foo"]
reduce - 各要素を連続的に処理して1つの値を得る
let h = ["Hoge", "fuga","piyo", "foo"].reduce("AAA"){ $0 + $1 }
print(h) // AAAHogefugapiyofoo