search
LoginSignup
3

More than 3 years have passed since last update.

posted at

updated at

忘備録-Swiftのジェネリクス

趣味で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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
3