【PHP】標準入力・標準出力チートシート【AtCoder】

## 1. はじめに

[【PHP】標準入力・標準出力を楽に記述するための工夫【AtCoder】](https://qiita.com/noko206/items/800649132ff050635ff0) という記事で、標準入力の受け取りと標準出力が楽に行える関数を作成しました。本記事では、それらの関数の使い方を具体例とともにまとめていきます。「この関数はどうやって使うの？」という疑問を解消できると幸いです。

## 2. 標準入力

標準入力で受け取る値は **整数** を想定しています。しかし、受け取る値が **文字列** や **小数** であっても記述方法は変わりません。整数なら ``ints()``、文字列なら ``strings()``、小数なら ``doubles()`` を使用してください。

本節では、以下の構成で説明をします。

> (1) 標準入力の形式

```
(2) 標準入力を受け取るPHPのコード ※標準入力を受け取る部分だけを抜粋
```

(3) 説明コメント

(4) 例題

---

### 2.1 単体の値として受け取る

> $a$
> $i　j$
> $x　y　z$

```php
[$a]         = ints();
[$i, $j]     = ints();
[$x, $y, $z] = ints();
```

単体の値として受け取る場合は [配列の分解](https://www.php.net/manual/ja/language.types.array.php#language.types.array.syntax.destructuring) を使用します。

* [ABC086A - Product](https://atcoder.jp/contests/abs/tasks/abc086_a)
* [A - Seats](https://atcoder.jp/contests/abc375/tasks/abc375_a)
* [A - Round decimals](https://atcoder.jp/contests/abc226/tasks/abc226_a)

---

### 2.2 配列として受け取る：配列が1行

> $N$
> $A_1　A_2　...　A_N$

```php
[$n] = ints();
$a = ints();
```

配列として受け取る場合、配列の分解は**使用しません。**

* [ABC081B - Shift only](https://atcoder.jp/contests/abs/tasks/abc081_b)
* [B - Piano 2](https://atcoder.jp/contests/abc355/tasks/abc355_b)
* [B - Default Price](https://atcoder.jp/contests/abc308/tasks/abc308_b)

---

### 2.3 配列として受け取る：配列がN行 & 配列が1個

> $N$
> $A_1$
> $A_2$
> $...$
> $A_N$

```php
// パターン１
[$n] = ints();
$a = [];
for ($i = 0; $i < $n; ++$i) {
    [$a[]] = ints(); // 配列の末尾に追加
}

// パターン２
[$n] = ints();
$a = [];
for ($i = 0; $i < $n; ++$i) {
    [$a[$i]] = ints(); // 添え字を指定して代入
}
```

配列の要素が $N$ 行あるので、for 文を $N$ 回まわします。配列の分解で要素を一つずつ取得して、空配列に追加していきます。

「パターン１」と「パターン２」は好きな方を使用してください。得られる配列の構造に違いはありません。

* [ABC085B - Kagami Mochi](https://atcoder.jp/contests/abs/tasks/abc085_b)
* [A - Glutton Takahashi](https://atcoder.jp/contests/abc364/tasks/abc364_a)
* [B - Grid Walk](https://atcoder.jp/contests/abc364/tasks/abc364_b)

---

### 2.4 配列として受け取る：配列がN行 & 配列が2個以上

> $N$
> $A_1　B_1$
> $A_2　B_2$
> $...$
> $A_N　B_N$

```php
// パターン１
[$n] = ints();
$a = [];
$b = [];
for ($i = 0; $i < $n; ++$i) {
    [$a[], $b[]] = ints(); // 配列の末尾に追加
}

// パターン２
[$n] = ints();
$a = [];
$b = [];
for ($i = 0; $i < $n; ++$i) {
    [$a[$i], $b[$i]] = ints(); // 添え字を指定して追加
}
```

配列が1個の場合とほとんど同じです。配列の分解で代入する変数を2つ, 3つと、必要に応じて増やします。

* [A - Humidifier 1](https://atcoder.jp/contests/abc383/tasks/abc383_a)
* [A - Scoreboard](https://atcoder.jp/contests/abc337/tasks/abc337_a)
* [B - Overlapping sheets](https://atcoder.jp/contests/abc318/tasks/abc318_b)

---

### 2.5 二次元配列として受け取る：列数が事前に与えられる場合

> $H　W$
> $A_{1,1}　A_{1,2}　...　A_{1,W}$
> $A_{2,1}　A_{2,2}　...　A_{2,W}$
> $...$
> $A_{H,1}　A_{H,2}　...　A_{H,W}$

```php
// パターン１
[$h, $w] = ints();
$a = [];
for ($i = 0; $i < $h; ++$i) {
    $a[] = ints(); // 配列の末尾に追加
}

// パターン２
[$h, $w] = ints();
$a = [];
for ($i = 0; $i < $h; ++$i) {
    $a[$i] = ints(); // 添え字を指定して追加
}
```

配列の要素が $N$ 行あるので、for 文を $N$ 回まわします。配列を一つずつ取得して、空配列に追加していきます。配列として標準入力を取得したいため、配列の分解は使用しません。

* [B - Nutrients](https://atcoder.jp/contests/abc356/tasks/abc356_b)
* [B - Discord](https://atcoder.jp/contests/abc303/tasks/abc303_b)
* [B - Binary Alchemy](https://atcoder.jp/contests/abc370/tasks/abc370_b)

---

### 2.6 二次元配列として受け取る：列数が各行で与えられる場合

> $N$
> $k_1　A_{1,1}　A_{1,2}　...　A_{1,k_1}$
> $k_2　A_{2,1}　A_{2,2}　...　A_{2,k_2}$
> $...$
> $k_N　A_{N,1}　A_{N,2}　...　A_{N,k_N}$

```php
// パターン１：Aが整数型の場合
[$n] = ints();
$k = [];
$a = [];
for ($i = 0; $i < $n; ++$i) {
    $line = ints();
    $k[] = array_shift($line); // 先頭の要素を取得して、配列からは削除
    $a[] = $line;
}

// パターン２：Aが文字列型 or 小数型の場合
[$n] = ints();
$k = [];
$a = [];
for ($i = 0; $i < $n; ++$i) {
    $line = strings();                 // k,Aで型が異なるため、文字列型として取得
    $k[] = (int)array_shift($line);    // 整数型にキャスト
    $a[] = array_map('strval', $line); // Aが小数型の場合は「doubleval」を使用
}
```

パターン１は二次元配列の要素が **整数型** の場合です。列数 $k_i$ と型が一致するため、``ints()`` で標準入力を取得します。
パターン２は二次元配列の要素が **文字列型** または **小数型** の場合です。列数 $k_i$ と型が異なるため、``strings()`` で標準入力を取得したあと、**キャスト** または ``array_map()`` を使用して適切な型に変換します。

* [B - Everyone is Friends](https://atcoder.jp/contests/abc272/tasks/abc272_b)
* [B - Maintain Multiple Sequences](https://atcoder.jp/contests/abc271/tasks/abc271_b)

---

### 2.7 クエリ問題

>> $Q$
>> $query_1$
>> $query_2$
>> $...$
>> $query_Q$
>
> ただし、$query_i$ は $i$ 個目のクエリを表しており、次の形式のいずれかで与えられる。
>
>> $1　k　x$
>> $2　k$

```php
[$q] = ints();
$queries = [];
for ($i = 0; $i < $q; ++$i) {
    $queries[] = ints();
}

// クエリを使用するとき
foreach ($queries as $query) {
    [$t] = $query;             // 先頭の値だけ取得 (クエリの種類)
    if ($t === 1) {
        [$_, $k, $x] = $query; // 種類に応じて適切な形式で変数に代入
    } else {
        [$_, $k] = $query;     // 種類に応じて適切な形式で変数に代入
    }
}
```

「ただし、$query_i$ は $i$ 個目のクエリを表しており、次の形式のいずれかで与えられる。」の部分は問題によって異なります。上記は [B - First Query Problem](https://atcoder.jp/contests/abc283/tasks/abc283_b) を例として挙げたものです。（標準入力を一部改変しています。）
クエリ問題は種類によって標準入力の形式が異なるため厄介です。対処法として、クエリを配列として受け取り、クエリを使用するときに適切な形式で変数に代入するのが良いかと思います。

* [B - First Query Problem](https://atcoder.jp/contests/abc283/tasks/abc283_b)
* [B - Append](https://atcoder.jp/contests/abc340/tasks/abc340_b)
* [C - Cards Query Problem](https://atcoder.jp/contests/abc298/tasks/abc298_c)

---

### 2.8 マルチテストケース問題

> $test_i$ は $i$ 番目のテストケースを意味する。
>
>> $T$
>> $test_1$
>> $test_2$
>> $...$
>> $test_T$
>
> 各テストケースは以下の形式で与えられる。
>
>> $N$
>> $A_1　A_2　...　A_N$

```php
// パターン１：solve()関数内でテストケースの標準入力を受け取る
function solve()
{
    [$n] = ints();
    $a = ints();
}

[$t] = ints();
for ($i = 0; $i < $t; ++$i) {
    solve();
}

// パターン２：for文内でテストケースの標準入力を受け取る
[$t] = ints();
for ($i = 0; $i < $t; ++$i) {
    [$n] = ints();
    $a = ints();
}
```

「各テストケースは以下の形式で与えられる」の部分は問題によって異なります。上記は [B - Multi Test Cases](https://atcoder.jp/contests/abc284/tasks/abc284_b) を例として挙げたものです。

テストケースが $T$ 個あるため、for 文を $T$ 回まわします。
パターン１はテストケースの標準入力の受け取りを関数に切り出す方法です。関数内では一つのテストケースのみに着目できるため、マルチテストケースではない普通の問題と同じようにコードが書けます。
パターン２は for 文内でテストケースの標準入力を受け取る方法です。

* [B - Multi Test Cases](https://atcoder.jp/contests/abc284/tasks/abc284_b)
* [E - (∀x∀)](https://atcoder.jp/contests/abc242/tasks/abc242_e?lang=ja)　※難易度高めです

---

### 2.9 一行に複数の型が存在

> $N$
> $A_1　S_1$
> $A_2　S_2$
> $...$
> $A_N　S_N$

※ $A_i$ は**整数**、$S_i$ は**文字列**

```php
// パターン１：array_map()関数で型変換
[$n] = ints();
$a = [];
$s = [];
for ($i = 0; $i < $n; ++$i) {
    [$a[], $s[]] = strings();
}
$a = array_map('intval', $a); // 整数型に変換

// パターン２：キャストで型変換
[$n] = ints();
$a = [];
$s = [];
for ($i = 0; $i < $n; ++$i) {
    [$a[], $s[]] = strings();
    $a[$i] = (int)$a[$i]; // 整数型に変換
}
```

上記は [B - Piano 3](https://atcoder.jp/contests/abc369/tasks/abc369_b) を例として挙げたものです。
一行に複数の型が存在する場合は、``strings()`` **で文字列として受け取り、** 必要に応じて型変換をしてください。

* [B - Piano 3](https://atcoder.jp/contests/abc369/tasks/abc369_b)
* [B - AtCoder Janken 2](https://atcoder.jp/contests/abc354/tasks/abc354_b)

## 3. 標準出力

### 3.1 単体の値を出力する

```php:コード
output(123);
output('abc');
output(3.14);
```

```:出力
123
abc
3.14
```

### 3.2 複数の値を空白区切りで出力する

```php:コード
output(123, 'abc', 3.14);
```

```:出力
123 abc 3.14
```

### 3.3 配列の値をN行で出力する

```php:コード
$n = 3;
$a = [10, 20, 30];

for ($i = 0; $i < $n; ++$i) {
    output($a[$i]);
}
```

```:出力
10
20
30
```

### 3.4 配列の値を1行で出力する

```php:コード
$a = [10, 20, 30];

output(...$a);
```

```:出力
10 20 30
```

## 4. まとめ

* 整数型、文字列型、小数型として値を受け取るときは [配列の分解](https://www.php.net/manual/ja/language.types.array.php#language.types.array.syntax.destructuring) を使用する
* 配列として値を受け取るときは配列の分解を**使用しない**
    * ただし、配列の標準入力が $N$ 行に分かれている場合は、配列の分解で要素を一つずつ受け取って空配列に追加していく
* 一行に複数の型が存在する場合は、``strings()`` で文字列型として受け取り、あとから適切な型に変換する
    * 単体の値を型変換する方法：キャスト
        * 整数型：``(int)``
        * 文字列型：``(string)``　※最初に文字列型として受け取るため、使用することはない
        * 小数型：``(double)``
    * 配列の値を型変換する方法：``array_map()`` 関数
        * 整数型：``array_map('intval', $array)``
        * 文字列型：``array_map('strval', $array)``　※最初に文字列型として受け取るため、使用することはない
        * 小数型：``array_map('doubleval', $array)``