AtCoder に Swift5.2 で挑戦する ための準備（主に入出力）

# はじめに

これまで[**ACoder**](https://atcoder.jp)で使用できる**Swift**のバージョンは**2.2**とかなり古く、はっきり言ってこれを使う気は起きず、[**Kotlin**](https://kotlinlang.org/docs/reference/)でコードを書いていました。しかし、2020.4.12に実施された[ABC162](https://atcoder.jp/contests/abc162)からオンラインジャッジ環境が一新され、Swiftもついに**5.2.1**にアップされました。 [^1]
これを機にSwiftで挑戦しようかと考え、入出力周りの**便利な関数**を揃えいこうと思います。

ちなみに、私のローカル環境は`Apple Swift version 5.2 (swiftlang-1103.0.32.1 clang-1103.0.32.29)`です。


<details><summary>_私が思うKotlinの欠点_</summary><div>

## Kotlinの欠点

Swift2.2と比べると、Kotlinの方が何倍もイケてると思いますが、私が思うKotlinの最大の欠点は『_配列の要素数にLong(型)を指定するとコンパイルエラーになる_』『_配列の添字にLong(型)を指定するとコンパイルエラーになる_』ことです。添字の範囲を超えているならいざ知らず、コンパイル時にLongだからと言ってエラーにするとは・・・面倒です。
Kotlinの整数型変換は`long.toInt()`でLongからIntへ、`int.toLong()`でIntからLongへ、簡単に書けます。また、配列の添字にLongを指定できるExtention(演算子オーバーロード)を書けば吸収できることも知っていますが、『**Int**が**32**ビットでそれ以上の整数を扱う場合は、**64**ビットの**Long**と使い分ける必要がある』であり、これで何度も**WA**を喰らいました。正直面倒です。（愚痴）
（Swiftの**Int**は環境依存となっていますが、私のローカル環境もAtCoderの新ジャッジ環境とも**64**ビットです）
</div></details>


## 標準入力

AtCoderではすべてのプログラムは**標準入力**からデータを受け取り、結果を**標準出力**に出力します。俗に言うstdin、stdoutというやつです。
Swiftでは`FileHandle.standardInput`が標準入力のファイルハンドルで、これを使うと次の**2**種類の方法でデータを入力できます。

```swift
let fileHandle = FileHandle.standardInput
let contentData = fileHandle.readDataToEndOfFile()       //その1 
let contentData = fileHandle.readData(ofLength: バイト長) //その2
let contentString = String(data: contentData, encoding: .utf8)! 
```

**1**番目の方法はファイルを**EOF**まで全部一度に読み込みます。**2**番目は入力するサイズを指定する方法です。
一般的にAtCoderの入力データはすべて**文字**で**形式が決まっている**ものの、例えば**1**行のデータ長が何バイトなのかなどは**分かりません**し、テストケースによっては入力データのサイズが**数メガ**バイトであることもあります。また、一度に読み込んだとしても、その後に**改行コード**で区切ったり、**1**行の中を**スペース**で区切ったりする必要があります。よって、私が考える入力方法の最善は上記のいずれでもなく、`readLine`メソッドを使うことです。シンプルに**1行を入力する**方法ですね。（**Java**の**Scannerクラス**相当がSwiftにもあればいいのでしょうが）

AtCoderを始めとする競プロの場合は、まず**ここから**ですから、この`readLine`の使い方に関しては、これまで多くの方が記事を書かれています。[^2] [^3] [^4]


AtCoderでの入力データの**代表的**な形式は、次の**3**パターンです。
1. **1**行で数値を**1つ**だけ受け取る
2. **1**行で数値を**2**〜**4**個受け取る
3. **1**行で文字列を**1つ**受け取る

また、**問題**と**データ**は**1**-indexedで与えられることが多いですが、プログラム上は**0**-indexedの方が都合が良かったりします。配列も**0**始まりですし。そこで、私が準備したメソッドは次の**3**パターンです。順番に説明します。

```swift:パターン1（1つシリーズ）
func getString() -> String { readLine()! }
func getInt() -> Int { Int(getString())! }
func getDouble() -> Double { Double(getString())! }
```

上記は一目瞭然なので説明は省略します。


```swift:パターン2（複数シリーズ）
func listOfString() -> [String] { getString().split(separator: " ").map { String($0) } }
func listOfInt(_ offset: Int = 0) -> [Int] { listOfString().map { Int($0)! + offset } }
func listOfDouble(_ offset: Double = 0.0) -> [Double] { listOfString().map { Double($0)! + offset } }

func listOfInt(_ offsets: Int ...) -> [Int] { listOfString().enumerated().map { Int($0.element)! + offsets[$0.offset % offsets.count] } }
```

`let A = listOfInt(-1)`のように`offset`を指定すれば、一律に**+**/**-**できます。Doblue型でこれを使う場面は無いかも知れませんが、省略可能ですから（そもそもDoubleを入力すること自体が無いかも）。
パターン**2**は数列のように配列として受け取る場合ですが、**タプル**で受け取りたい場合があります。例えば平面座標の大きさの`H,W`とか、グラフの頂点と辺の数`N,M`とか。そこで、最後にパターン**3**です。

```swift:パターン3（タプルシリーズ）
extension Array where Element == Int {
    func toTupple() -> (Int, Int) { (self[0], self[1]) }
    func toTupple() -> (Int, Int, Int) { (self[0], self[1], self[2]) }
    func toTupple() -> (Int, Int, Int, Int) { (self[0], self[1], self[2], self[3]) }
}
```

**Int**配列の**extension**として実装しました。こうすることで、パターン**2**との組み合わせが実現できます。例えば、
`let (u, v, c) = listOfInt(-1, -1, 0).toTupple()` と書くと、**「グラフ問題の辺（を結ぶ頂点）と辺のコスト」**を(**0**-indexedにして)簡単に受け取ることができます。

@y_mazunさんの記事 [^5] で演算子(オーバーロード)で**タプル**を受け取れる実装を公開しておられます。これも便利ですね。パターン**3**もそれに倣うか今後考えます。


## 標準出力

標準出力は結果の出力先で、入力に比べると至ってシンプルです。大体は数値や文字列を**1**つ出力するパターンが多く、これはシンプルに`print`メソッドで書きます。

```swift:パターン1
var ans = 0
   :    :
print(ans)
```

`print`はデフォルトで**改行コード**が付加されるので、**C++**のように`\n`を付ける必要はありません。逆に`\n`を付けたく無い場合は、`print(・・・・, terminator: "")`と書くことができますが、AtCoderでは最後に改行コードが必須です。
また、**2**〜**3**個の数値を横に並べて出力する場合は、次のように書きます。

```swift:パターン2
var a = 0, b = 0, c = 0
   :    :
print(a, b, c)
```

これで`a b c`のようにスペース区切りで横に並び、最後に改行コードが付加されます。改行区切りで縦に並べたい場合は、`print(a, b, c, separator: "\n")` とすることで可能です。

最後のパターンは、横もしくは縦（複数行）に多数の数値を出力する場合で、以下のように書きます。

```swift:パターン3
var ans = [Int]()
   :    :
print(ans.map(String.init).joined(separator: " "))  //スペース区切り横並び
print(ans.map(String.init).joined(separator: "\n")) //改行区切り（縦並び）
```

大事なことは、**I**/**O**は遅いので`print`をループさせないことです。過去に数値を最大**20**万行出力するという**問題** [^6] もありました。**Int**を**20**万行出力するときに、`print`をループさせた場合と、`joined`して`print`**1**回の場合を性能を比較してみました。

<details><summary>_テストコードを表示_</summary><div>

```swift:テストコード
import Foundation

let main: () = {
    let N = 200000
    let A = ([Int])(0 ..< N)
    
    //Loop
    let t1 = measureTimeSeconds {
        for a in A { print(a) }
    }
    
    //Join
    let t2 = measureTimeSeconds {
        print(A.map(String.init).joined(separator: "\n"))
    }

    printErr("Loop: \(t1)\nJoin: \(t2)\n")
    
}()

func printErr(_ s: String) {
    FileHandle.standardError.write(s.data(using: .utf8)!)
}

func measureTimeSeconds(block: (() -> Void)) -> Double {
    let startTime = Date()
    block()
    return -startTime.timeIntervalSinceNow
}
```
</div></details>

```log:結果[秒]
Loop: 0.07886505126953125
Join: 0.03861498832702637
```

ちょっと意外な結果でした。確かに**join**方式の方が倍ぐらい早いですが、**loop**方式でも**100**ミリ秒以下だったので、これならもしかしてAtCoderでの実行時間制限（通常**2**秒）に収まるかも知れません。（_Kotlinだと確実にTLEです。_）


```log:Kolinでの結果[秒]
Loop: 0.805783205
Join: 0.086151367
```

## その他

入出力以外で、**有ると便利**な関数をいくつか準備しておきます。

```swift:便利関数
extension Int {
    var isEven: Bool { self.isMultiple(of: 2) }
    var isTrue: Bool { self != 0 }
}

func + (left: Int, right: Bool) -> Int { left + (right ? 1 : 0) }
```

まずは**偶奇**を判定する関数です。普通は`n % 2 == 0`のように判定しますが、**Int**のプロパティとして実装しました。**偶奇**を判定する問題は多いです。
`if n.isEven { 偶数の場合 } else { 奇数の場合 }` 

同様に、数値で**真偽**を判定したい場面もあります。(C++ライクに)**0**が**偽**、**0**以外が**真**です。
`if n.isTrue { 真の場合 } else { 偽の場合 }` 

使う場面は少ないと思いますが、最後は**Int**と**Bool**の**加算**です。**true**を**1**、**false**を**0**として足し算します。
`let a = 10 + true` → `11`
 

<2021.9.19追加>
配列に関する便利関数を追加しました。

```swift:便利関数（その2）
extension Array {
    init(_ size: Int, _ initValue: ((Int) -> Element)) {
        self = [Element]()
        for index in 0 ..< size {
            let value = initValue(index)
            self.append(value)
        }
    }
    init(_ size: Int, _ initValue: (() -> Element)) {
        self = [Element]()
        for _ in 0 ..< size {
            let value = initValue()
            self.append(value)
        }
    }
    static func += (lhs: inout Self, rhs: Element) { lhs.append(rhs) }
}
```

Kotlin風に配列を初期化するinit（コンストラクタ）2つと、配列に要素を追加する`+=`演算子です。
配列の初期化は、
`let A = [String](N) { getString() }` や、
`let B = [(Int, Int)](N) { n in (n, getInt()) }`のように書き、入力データを読みながら配列に詰め込むことができます。
`+=`演算子は見ての通り、配列の末尾に要素を追加（`append`）します。例えば、整数配列Cに要素3を追加するときに、
`C += 3`と書きます（`C.append(3)`と等価）。



# 終わりに

いくつか関数を準備できましたので、これから**Swift** _de_ **AtCoder**を満喫したいと思います。また、この記事を読んでくださった方々の参考になればと思います。
「_競プロやるならC++だ_」と言われていますが、私はSwift（_もしくはKotlin_）でまずは緑コーダを目指します。
もっと良い方法とかありましたら、ぜひ教えていただけると嬉しいです。

### 以上


[^1]: [AtCoder 新ジャッジシステム（主にKotlin）](https://qiita.com/nak435/items/2cab8cc9918017b9564b)
[^2]: [paizaの問題をSwiftで挑戦するときの標準入出力](https://qiita.com/st43/items/402dc68a0e0d2c6686b9)
[^3]: [Swift5.0.1でAtcoder！](https://qiita.com/taittide/items/4c25f36ff96ec01a4512)
[^4]: [AtCoder に登録したら解くべき精選過去問 10 問を Swift 5 で解いてみた](https://qiita.com/conf8o/items/8f2130510a9deaffa312)
[^5]: [Swiftで競技プログラミング](https://qiita.com/y_mazun/items/dc2a0cad8da1c0e88a40)
[^6]: [ABC159 D - Banned K](https://atcoder.jp/contests/abc159/tasks/abc159_d)