Swift/pattern

Swift/パターン/タプル

タプル(tuple)とは複数個のデータを組にしたもの。 構造体とは異なり、メソッドやプロパティを付け加えることはできない。 値型のデータであり、代入によって新しいインスタンスが生成される。

let a:(String, Int) = ("monkey.jpg", 161_022")
let b = ("monkey.jpg", 161_022)  // 型推論を利用した
let c = ("cat.jpg", 1024, 768)   // 要素数は2個に限らない
var d:(String, Int, Int) = c     // 型が同じなので代入できる。タプルの代入はコピー。
d = a;                           // 型が異なるのでエラー。

タプルの各要素には ".0", ".1" のように、"."(ドット)の後に10進整数を指定してアクセスする。 この数字の部分を変数にすることはできない。

print("\(d.0): \(d.1) x \(d.2)")   // cat.jpg: 1024 x 768 と表示される。
d.2 = 800                          // dは変数(var)なので代入できる。
print("\(d.0): \(d.1) x \(d.2)")   // cat.jpg: 1024 x 800 と表示される。

タプルは、相互に等しいかどうか調べる方法が提供されていない。次の例でもエラーとなる。

(1,2) == (1,2)

タプルは異なる型の複数の要素を含むことができるので、比較することは単純ではないためだが、 ただし、「整数2つを含むタプル」などのように限定すれば演算子を定義することは可能である。

タプルと代入操作

タプルは同じ型を持つ変数に代入が可能であり、一度の代入で各要素を別々の変数や定数に格納できる。

let photo = ("tiger.jpg", 640, 800)
let (file,width,height) = photo   // 定数file, width, heightを定義し、即、代入する。
print("\(file): \(width) x \(height)") // tiger.jpg 640 x 800 と表示される。
let (f,_,h) = photo      // .0, .2 の値だけ取り出し、残り(.1)は捨てる

タプルには任意のインスタンスを含めることができるので、タプルを含めることも可能。

let pic = ("snake.jpg", (640,480))   // タプルを含むタプル
let (file,(w,h)) = pic               // 要素を一度に取り出す
let (name,_) = pic                   // 一部の値だけを取り出す

タプルを使うと複数の要素を同時に代入できる。

var x = 1, y = 2
(x,y) = (y,x)
print("x=\(x), y=\(y)")     // x=2, y=1 と表示される

タプルを返す関数

関数から複数の値を返したいときにタプルを使うとよい。

func BMI(tall:Double, _ weight:Double) -> (Double,Double) {
  let ideal = 22.0
  let t2 = tall * tall / 10000.0    // cmをmに換算する
  let index = weight / t
  return (index,ideal * t2)
}
let result = BMI(177.0, 80.0)
print(result)        // (25.5354, 68.9238) と表示される

値が1個だけのタプルは、その値自体と同じと見なされる。 関数の返り値がないことを、要素が1個もないタプル()で表現する。 Voidは()の別名である。

キーワード付きのタプル

関数の引数にキーワードをつけることができるように、タプルにもそれぞれの要素にキーワードをつけることができる。

let photo = (file:"tiger.jpg",width:640,height:800)

"." (ドット)の後ろにキーワードをつけて要素にアクセスできる。

print(photo.0)             // tiger.jpg と表示される
print(photo.file)          // tiger.jpg と表示される

キーワードは全ての要素につける必要はなく、一部の要素にだけにつけても構わない。 同じキーワードがあっても構わないが、その要素にアクセスするには注意が必要になる。

let memo = (subject:"会合", 2016, 5, 16)
let addr = (name:"名無しさん", mail:"nobody@xxx",mail:"nobody@yyy")

キーワード付きのタプルと代入

キーワードの付いたタプルは、同じキーワードのついたタプルは、キーワードのないタプルとの間でしか代入ができない。

let img = ("phoenix.jpg", 1200, 800)
var v1: (String,Int,Int) = img
var v2: (file:Strign,width:Int, height:Int) = img
var v3: (image:Strign,x:Int, y:Int) = img
let photo = (file:"tiger.jpg",width:640,height:400)
v1 = photo
v2 = photo
v3 = photo  // エラー。キーワードが異なる。
v3 = photo as (String,Int,Int)  // 一旦キーワード無しのタプルにcastすれば代入できる。

キーワードがついたタプルは、要素の順番が違っていても同じキーワード同士で代入できる。

var x = 0, y = 0, z = 0
(red:x, green:y, blue: z) = (blue:5, red:250, green:220) // x=250,y=220,z=5になる
var t: (red:Int, green:Int, blue:Int) = (blue:80, 0, 200) // t=(red:0,green:200,blue:80)になる

タプルに型の別名をつけることができる。

typealias PhotoFile = (file:String,width:Int,height:Int)
typealias ImageFile = (image:String,x:Int,y:Int)
var w: PhotoFile = ("snake.jpg",width:768,height:1024)
print(w.file)      // snake.jpg と表示される

キーワード付きのタプルを返す関数

func BMI(tall:Double, _ weight:Double) -> (index:Double,ideal:Double) {
  let ideal = 22.0
  let t2 = tall * tall / 10000.0    // cmをmに換算する
  let index = weight / t
  return (index,ideal * t2)
}
let a = BMI(177.0, 80.0)
print("BMI=\(a.index), target=\(a.ideal)")   // BMI=25.5354, target=68.9238 と表示される
var b:(Double,Double) = BMI(177.0, 80.0)
print("\(a.index))   // エラー。bはキーワードを持たないタプルだから。

タプルをswitch文で使う

case文のラベルにタプルを記述することが可能。ただし、記述されるタプルはすべて同じ型である必要がある。

switch day {               // (Int,Int) 型の場合
  case (1,1) : print("元旦")
  case (2,11) : print("建国記念日")
  case (5,3) : print("憲法記念日")
  default: break;
}

ある範囲の日付を表すために、範囲演算子を使って区間を記述することができる。

switch day {               // (Int,Int) 型の場合
  case (1,1...5) : print("正月休み")  // 1/1 〜 1/5
  case (2,11) : print("建国記念日")
  case (4,29), (5,2...6) : print("連休")  // 4/29, 5/2-5/6
  default: break;
}

値を列挙するだけではなく、タプルの要素を取り出して処理に使うこともできる。 caseラベル無に let や var を記述して、case節の内部でのみ有効な変数や定数を宣言できる。 初期値は対応するタプルの要素の値となる。

switch day {               // (Int,Int) 型の場合
  case (1,1) : print("元旦")
  case (8,let d) : print("8/\(d) は夏休みです")
  case (12,_) : print("12月はずっと冬休みです")
  deefault: break;
}

switch文でwhere節を使う

switch文でタプルの要素に対して条件をつけることができる。

// dayOfWeek(1, d) は1月d日が月曜日の場合に1を返す関数であるとする
switch day {
  case (1, let d) where d >= 8 && d <= 14 && dayOfWeek(1,d) == 1: print("成人の日")
  case (8,_): print("夏休み")
  case (let m, let d) where dayOfWeek(m,d) == 0: print("\(m)月\(d)日は日曜日")
  default: break
}

上記のように要素毎にletを付けてもいいが、まとめて宣言することもできる。

  case let (m,d) where dayOfWeek(m,d) == 0: print("\(m)月\(d)日は日曜日")

どのラベルに一致したかによって値を持たない可能性があるので、 定数や変数を使うラベルは、別なラベルと","で区切って並べることができない。 また、同じ理由から上のラベルから fallthroughで遷移することができない。

switch day {
  case (1, let y), (2,2) : print("\(y)")  // エラー。(2,2)のときyの値が不明。
  case (2,_): fallthrough                 // エラー。zの値が不明になるので。
  case (let z,2) : print("\(z)");
  default: break;
}

オプショナル型をswitch文で使う

typealias People = (String,Int?)
switch m {
  case let (name,age?) where age >= 18: print("\(name), 免許が取れる")
  case let (name,(15...18)?): print("\(name), 高校生")
  case let (let name,nil): print("\(name), 年齢不明") // 第2要素がnil
  case let (name, 15?) : print("\(name), 中学3年生") // (String, 15)にマッチする
  default: break
}

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2016-05-25 (水) 14:52:31 (1474d)