最近プロコンに取り組むことが多いのですが、度々出てくる定番の処理があるなと感じたのでまとめてみます
出てくる処理は初歩的なものが多いので、これからSwiftでプロコン挑戦したいと思っている、あるいはSwiftを初めて触るよ、なんて人には役立つと思います
使えそうなものがあればぜひご利用ください
もっと良い書き方があるよ!というものがあればぜひコメント下さい
チートシート
入力処理
来る値が限定的なのでバンバンForced Unwrappingしています(アレルギーの人いたらゴメンナサイ)。
プロダクトコードでは真似せずに、適切にOptional Bindingなり何なりしてください。
// 標準入力一行から単一のStringを取得
let string = readLine()!
// 標準入力一行から単一のIntを取得
let N = Int(readLine()!)!
// 標準入力一行からスペース区切りの複数のIntを取得
// 2行目は別にやらなくてもいいけど、名前がついていたほうが個人的に扱いやすいと思うのでやってます
let nums = readLine()!.split(separator: " ").map { Int($0)! }
let (M, H, W, P, Q) = (nums[0], nums[1], nums[2], nums[3], nums[4])
// 標準入力N行からスペース区切りの複数のIntを取得
var someArray = [[Int]]()
for _ in 0..<N { // (0..<N).forEach { でもOK、indexを使いたい場合は"_"を"i"とかで受ける
let input = readLine()!.split(separator: " ").map { Int($0)! }
someArray.append(input)
}
データ構造
配列
これ使っておけば大抵のことはできる。
初期化
// 空の一次元配列を生成
var someArray = [Int]() // => []
// 空の二次元配列を生成
var anotherArray = [[String]]() // => [[]]
// 初期値を指定しつつN個の要素を持った配列を生成
// 初期盤面が決まっているとか、とりあえずスコア0にしておくとか
var zeroArray = [Int](repeating: 0, count: N) // => [0, 0, ... , 0]
// StringからCharacterの配列を生成
// 文字列を一文字ずつ扱いたいときに
var charArray = Array("##...") // => ["#", "#", ".", ".", "."]
要素の追加、削除
// 最後に要素を追加
var array1 = [0, 1, 2]
array1.append(3) // => [0, 1, 2, 3]
// 最後に配列を連結
var array2 = [0, 1]
array2.append(contentsOf: [2, 3]) // => [0, 1, 2, 3]
array2 += [2, 3] // => [0, 1, 2, 3]
// 指定したindexに要素を追加
var array3 = [0, 1, 3]
array3.insert(2, at: 2) // => [0, 1, 2, 3]
// 指定したindexの要素を削除、削除した要素が返り値になる
var array4 = [0, 1, 2]
let one = array4.remove(at: 1) // => [0, 2]
// 最初の要素を削除、削除した要素が返り値になる
var array5 = [0, 1, 2]
let zero = array5.removeFirst() // => [1, 2]
// 最後の要素を削除、削除した要素が返り値になる
var array6 = [0, 1, 2]
let two = array6.removeLast() // => [0, 1]
配列の性質
// 要素が空か(要素数が0か)
[].isEmpty // => true
[0].isEmpty // => false
// 要素数の取得
[0, 1, 2].count // => 3
要素へのアクセス
let array = [0, 1, 2]
// 指定したindexの要素取得
array[2] // => 2
// 指定したindexの要素を更新
array[2] = 3
// 指定したindexのスライス取得
array[0...1] // => [0, 1]
検索
let array = [5, 6, 7]
// 指定した要素、または条件を満たす要素を含むか
array.contains(7) // => true
array.contains { $0 < 0 } // => false
// 条件を満たす最初、または最後の要素
array.first { $0 > 5 } // => Optional(6)
array.last { $0 > 5 } // => Optional(7)
array.last { $0 < 0 } // => nil
// 指定した要素の最初、または最後のindex
array.firstIndex(of: 6) // => Optional(1)
array.firstIndex(of: 1) // => nil
array.lastIndex(of: 5) // => Optional(0)
// 最大値、最小値
array.max() // => 7
array.min() // => 5
変形
// すべての要素に式を適用した配列を生成
var array1 = [5, 6, 7]
let mappedArray = array1.map { $0 + 1 } // => [6, 7, 8]
// 配列の各要素に式を適用した上で次元を一つ落とした配列を生成
// s.flatMap(transform) is equivalent to Array(s.map(transform).joined()) らしい
// https://developer.apple.com/documentation/swift/array/flatmap(_:)-i3mr より
var array2 = [[1], [2, 2], [3, 3, 3]]
let flatMappedArray1 = array2.flatMap { $0 } // => [1, 2, 2, 3, 3, 3]
let flatMappedArray2 = array2.flatMap { $0.map { $0 + 1 } } // => [2, 3, 3, 4, 4, 4]
// 変形の結果がnilになるものを除いた配列を生成
var array3 = ["1", "2", "three", "///4///", "5"]
let compactMappedArray = array3.compactMap { Int($0) } // => [1, 2, 5]
// 条件を満たす要素のみの配列を生成
var array4 = [0, 1, 2, 3]
let filteredArray = array4.filter { $0 > 0 && $0 < 3 } // => [1, 2]
// ソート(デフォルトで昇順)
var array5 = [2, 1, 0, 3]
array5.sort() // => [0, 1, 2, 3]
array5.sort(by: >) // => [3, 2, 1, 0]
// 畳み込み
var array6 = [1, 2, 3]
array6.reduce(0, +) // => 6
array6.reduce(1) { $0 * $1 + 1 } // => 16
集合
要素の重複を許したくない場合に使える。
下の例では便宜上配列っぽい書き方をするが、実際には順序はない。
// 空の集合を生成
var someSet = Set<Int>() // => []
// 配列を元に集合を生成、重複している要素は一つになる
var setFromArray = Set([0, 0, 1, 2, 2, 2]) // => [0, 2, 1]
// 要素の追加
setFromArray.insert(3) // => [3, 0, 1, 2]
// 要素の削除、削除した要素が返り値になる(Optional)
let optionalTwo = setFromArray.remove(2) // => [3, 0, 1]
// 和集合
let unionSet = Set([0, 2, 4]).union([0, 1, 2]) // => [0, 1, 2, 4]
// 共通部分
let intersectionSet = Set([0, 2, 4]).intersection([0, 1, 2]) // => [0, 2]
// 差集合
let differenceSet = Set([0, 2, 4]).subtracting([0, 1, 2]) // => [4]
// 後は配列と同様に、
// isEmpty, count, contains, max, min, map, flatMap, compactMap, filter, sorted, reduce
// などがある
辞書
key-valueのセットを持ちたい場合に使える。
// 空の辞書を生成
var emptyDictionary = [Int: String]() // => [:]
// 指定した要素を持つ辞書を生成
var someDictionary = ["a": "apple", "b": "boss", "c": "cool"] // => 見た通り
// keyを指定して値の取得
someDictionary["a"] // => Optional("apple")
someDictionary["d"] // => nil
// keyを指定して値の取得(keyが存在しないときは指定したデフォルト値を返す)
someDictionary["b", default: "notFound"] // => "boss"
someDictionary["e", default: "notFound"] // => "notFound"
// 新しいkey-valueを追加
someDictionary["d"] = "dlang-kun" // => ["a": "apple", "b": "boss", "c": "cool", "d": "dlang-kun"]
// 要素の更新
someDictionary["a"] = "age" // => ["a": "age", "b": "boss", "c": "cool", "d": "dlang-kun"]
// 要素の削除、削除した要素が返り値になる(Optional)
let optionalCool = someDictionary.removeValue(forKey: "c") // ["a": "age", "b": "boss", "d": "dlang-kun"]
// 後は配列と同様に、
// isEmpty, count, contains, max, min, map, flatMap, compactMap, filter, sorted, reduce
// などがある
ループ
for
とwhile
という大体どの言語にもあるループが存在する。
// N回ループする
for i in 0..<N {
// 何かしらの処理
}
// 配列の要素の数だけ、要素を取り出しながらループする
for element in [1, 2, 3] {
// 何かしらの処理
}
// 条件を満たし続ける限りループする
var someFlag = true
while(someFlag) {
// 何かしらの処理
}
// ループから抜ける、続ける
let (H, W) = (4, 5)
loop1: while(true) { // ループにはラベルをつけることができる
loop2: for row in 0..<H { // ループにはラベルをつけることができる
for col in 0..<W {
if (/* 何かしらの条件 */) {
break loop1 // ラベルを指定するとそのラベルがついたループを抜ける
} else if (/* 何かしらの条件 */) {
continue loop2 // ラベルを指定するとそのラベルがついたループを続ける
} else if (/* 何かしらの条件 */) {
break // ラベルを指定しないと一番内側のループを抜ける
} else {
// 何かしらの処理
}
}
}
// 指定した条件を満たす要素のみでループする
for i in 0..<100 where i % 2 == 0 {
// iは0以上100未満の偶数
}
出力処理
答えを標準出力に出さないといけないので、そのときに使えるスニペットたち。
// 文字列/数値を1行出力する
print("answer")
print(100)
// 数値の一次元配列を1行1要素で要素数行出力する
print([3, 5, 7].map {$0.description}.joined(separator: "\n"))
// 数値の二次元配列を各行スペース区切りで要素数行出力する
[[0, 0], [1, 1], [2, 2]].forEach { print($0.map { $0.description }.joined(separator: " ")) }
おわりに
チートシートのつもりで書き始めたのになんだか劣化公式ドキュメントみたいになってしまったなあ(特にデータ構造のところ)
ここで挙げたもの以外にも関数はたくさんあるし、不正な引数を渡したときの挙動なんかはこの記事ではほとんど扱ってないので、公式ドキュメントもぜひ合わせて御覧ください
この記事が何かしらの役に立てば幸いです
プロコン楽しみましょう!