構造体
読了目安 約3分
座標や区間を struct にまとめる。Comparable に準拠して sorted() で並べられるようにする。
この章の目次
座標、グラフの辺、区間。 競プロの入力は「意味を持った値の組」だらけです。 組に名前と操作を持たせて自作の型にするのが構造体です。
構造体を定義する
構造体は、値と処理をまとめた自作の型です。
struct で定義し、中に持つ変数をプロパティと呼びます。
struct Point {
var x: Int
var y: Int
}
let p = Point(x: 3, y: 5)
print(p.x, p.y)Point(x: 3, y: 5) のような値の作り方をイニシャライザと呼びます。
プロパティを順に埋めるだけのイニシャライザは、Swift が自動で用意します (memberwise initializer)。
メソッド
構造体の中に書いた関数をメソッドと呼びます。 メソッドは自分のプロパティを名前だけで参照できます。
struct Point {
var x: Int
var y: Int
// マンハッタン距離 (縦と横の移動量の合計)
func distance(to other: Point) -> Int {
return abs(x - other.x) + abs(y - other.y)
}
}
let a = Point(x: 1, y: 2)
let b = Point(x: 4, y: 6)
print(a.distance(to: b))mutating
自分のプロパティを書き換えるメソッドには mutating を付けます。
struct Counter {
var value = 0
mutating func add(_ n: Int) {
value += n
}
}
var c = Counter()
c.add(1)
c.add(2)
print(c.value)構造体は値型なので、let で作ると中身も変更できません (値型と inout)。
mutating メソッドを呼べるのは var で作った値だけです。
Comparable でソート可能にする
Comparable に準拠すると、自作の型に sorted() や < がそのまま使えます。
書くのは < の定義 1 つだけです。
struct Entry: Comparable {
var score: Int
var name: String
static func < (lhs: Entry, rhs: Entry) -> Bool {
return (lhs.score, lhs.name) < (rhs.score, rhs.name)
}
}
let entries = [
Entry(score: 90, name: "carol"),
Entry(score: 80, name: "bob"),
Entry(score: 90, name: "alice"),
]
for e in entries.sorted() {
print(e.name, e.score)
}比較の中身には、タプルと型推論 の辞書順比較がそのまま使えます。
試してみよう: < の中身を (lhs.name, lhs.score) < (rhs.name, rhs.score) に変えて、名前順になることを確かめてください。
Comparableのような「型が満たすべき約束」をプロトコルと呼びます。: Comparableと書いて準拠すると、<から>==なども導かれます。
タプルとの使い分け
- その場限りの組は、タプルで十分
- メソッドを持たせたい組、何か所にも登場する組は、構造体
学びどころ
| 概念 | 一言まとめ |
|---|---|
struct | 値と処理をまとめた自作の型 |
| プロパティ | 構造体が持つ変数 |
| イニシャライザ | 値の作り方。memberwise は自動で付く |
mutating | プロパティを書き換えるメソッドの印 |
Comparable | < を 1 つ書けば sorted() が使える |
演習
1 行目に時刻を表す 3 つの整数 H、M、S が空白区切りで入力されます。 2 行目に経過秒数 X が入力されます。
X 秒後の時刻を 時:分:秒 の形式で出力してください。
24 時間制で、ゼロ埋めはしません。
制約: 0 ≤ H ≤ 23、0 ≤ M, S ≤ 59、0 ≤ X ≤ 10^9
入力例:
10 10 10
5
出力例:
10:10:15模範解答
struct Clock {
var hour: Int
var minute: Int
var second: Int
mutating func advance(by seconds: Int) {
let total = (hour * 3600 + minute * 60 + second + seconds) % 86400
hour = total / 3600
minute = total % 3600 / 60
second = total % 60
}
}
let parts = readLine()!.split(separator: " ").map { Int($0)! }
let x = Int(readLine()!)!
var clock = Clock(hour: parts[0], minute: parts[1], second: parts[2])
clock.advance(by: x)
print("\(clock.hour):\(clock.minute):\(clock.second)")時刻を一度「0 時 0 分 0 秒からの秒数」に直すのがコツです。
1 日は 86400 秒なので、% 86400 で日付をまたぐ分を捨てられます。