Swift教室 Swift と競技プログラミングの教室

タプルと型推論

読了目安 約3分

値の組をタプルで束ねる。辞書順の比較と組み合わせて、多キーのソートを一行で書く。

この章の目次

「名前と点数」「x 座標と y 座標」のような値の組は、競プロのあらゆる場面に現れます。 組をそのまま 1 つの値として運ぶ道具がタプルです。

タプルを作る・分解する

タプルは、複数の値を括弧で束ねた値です。

Swift
let point = (3, 5)
print(point.0, point.1)

let (x, y) = point
print(x + y)

中身は .0 .1 で取り出せます。 let (x, y) = point と書くと、まとめて別々の定数に分解できます。

型は書かなくても、中身から (Int, Int) と型推論されます。 配列と違って、(1, "alice") のように違う型も混ぜられます。

ラベル付きタプル

要素に名前を付けると、番号より読み間違えにくくなります。

Swift
let user = (name: "alice", score: 90)
print(user.name, user.score)

関数から複数の値を返す

戻り値の型をタプルにすると、関数が複数の値を一度に返せます。

Swift
func minMax(_ a: [Int]) -> (min: Int, max: Int) {
    return (a.min()!, a.max()!)
}

let r = minMax([3, 1, 4, 1, 5])
print(r.min, r.max)

a.min()!! は「配列が空ではない」とみなす印です。

タプルの比較

同じ型どうしのタプルは < で比較できます。 先頭の要素から比べ、等しければ次の要素で決まります。 この順序を辞書順と呼びます。

Swift
print((1, 5) < (2, 3))
print((2, 5) < (2, 3))
print(("alice", 90) < ("alice", 95))

2 番目の値でソート

sorted { 条件 } と書くと、並び順を自分で決められます。 $0$1 に比較される 2 つの要素が入り、true を返すと $0 が前に並びます。 この書き方の仕組みは クロージャと高階関数 で扱います。

Swift
let items = [("alice", 90), ("bob", 80), ("carol", 90)]
let ranking = items.sorted { ($0.1, $0.0) < ($1.1, $1.0) }
for item in ranking {
    print(item.0, item.1)
}

試してみよう: bob の点数を 95 に変えて、末尾に移ることを確かめてください。

多キーソートの定石は、比較したいキーを優先順にタプルへ並べることです。 上の例は「点数の昇順、同点なら名前の昇順」になります。

降順にしたいキーは、符号を反転して並べます。

Swift
// 点数の降順、同点なら名前の昇順
let ranking = items.sorted { (-$0.1, $0.0) < (-$1.1, $1.0) }

学びどころ

概念一言まとめ
タプル複数の値を括弧で束ねた値。違う型も混ぜられる
ラベル付きタプル(name: "alice", score: 90) で名前アクセス
多値返却戻り値の型をタプルにする
辞書順の比較先頭の要素から順に比べる
多キーソート比較キーをタプルに並べる。降順は符号を反転

演習

1 行目に人数 N が入力されます。 続く N 行に、名前 S と点数 P が空白区切りで入力されます。

点数の降順に、同点の場合は名前の昇順に並べ替え、「名前 点数」を 1 行ずつ出力してください。

制約: 1 ≤ N ≤ 100。名前は英小文字のみで、全員異なります。0 ≤ P ≤ 100。

テキスト
入力例:
4
carol 70
alice 90
dave 90
bob 80

出力例:
alice 90
dave 90
bob 80
carol 70
模範解答
Swift
let n = Int(readLine()!)!
var people: [(name: String, score: Int)] = []
for _ in 0..<n {
    let parts = readLine()!.split(separator: " ")
    people.append((name: String(parts[0]), score: Int(parts[1])!))
}
let ranking = people.sorted { (-$0.score, $0.name) < (-$1.score, $1.name) }
for p in ranking {
    print(p.name, p.score)
}

点数の符号を反転したタプルを比べると、「点数の降順、同点なら名前の昇順」になります。