1. はじめに
【PHP】標準入力・標準出力を楽に記述するための工夫【AtCoder】 という記事で、標準入力の受け取りと標準出力が楽に行える関数を作成しました。本記事では、それらの関数の使い方を具体例とともにまとめていきます。「この関数はどうやって使うの?」という疑問を解消できると幸いです。
2. 標準入力
標準入力で受け取る値は 整数 を想定しています。しかし、受け取る値が 文字列 や 小数 であっても記述方法は変わりません。整数なら ints()
、文字列なら strings()
、小数なら doubles()
を使用してください。
本節では、以下の構成で説明をします。
(1) 標準入力の形式
(2) 標準入力を受け取るPHPのコード ※標準入力を受け取る部分だけを抜粋
(3) 説明コメント
(4) 例題
2.1 単体の値として受け取る
$a$
$i j$
$x y z$
[$a] = ints();
[$i, $j] = ints();
[$x, $y, $z] = ints();
単体の値として受け取る場合は 配列の分解 を使用します。
2.2 配列として受け取る:配列が1行
$N$
$A_1 A_2 ... A_N$
[$n] = ints();
$a = ints();
配列として受け取る場合、配列の分解は使用しません。
2.3 配列として受け取る:配列がN行 & 配列が1個
$N$
$A_1$
$A_2$
$...$
$A_N$
// パターン1
[$n] = ints();
$a = [];
for ($i = 0; $i < $n; ++$i) {
[$a[]] = ints(); // 配列の末尾に追加
}
// パターン2
[$n] = ints();
$a = [];
for ($i = 0; $i < $n; ++$i) {
[$a[$i]] = ints(); // 添え字を指定して代入
}
配列の要素が $N$ 行あるので、for 文を $N$ 回まわします。配列の分解で要素を一つずつ取得して、空配列に追加していきます。
「パターン1」と「パターン2」は好きな方を使用してください。得られる配列の構造に違いはありません。
2.4 配列として受け取る:配列がN行 & 配列が2個以上
$N$
$A_1 B_1$
$A_2 B_2$
$...$
$A_N B_N$
// パターン1
[$n] = ints();
$a = [];
$b = [];
for ($i = 0; $i < $n; ++$i) {
[$a[], $b[]] = ints(); // 配列の末尾に追加
}
// パターン2
[$n] = ints();
$a = [];
$b = [];
for ($i = 0; $i < $n; ++$i) {
[$a[$i], $b[$i]] = ints(); // 添え字を指定して追加
}
配列が1個の場合とほとんど同じです。配列の分解で代入する変数を2つ, 3つと、必要に応じて増やします。
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}$
// パターン1
[$h, $w] = ints();
$a = [];
for ($i = 0; $i < $h; ++$i) {
$a[] = ints(); // 配列の末尾に追加
}
// パターン2
[$h, $w] = ints();
$a = [];
for ($i = 0; $i < $h; ++$i) {
$a[$i] = ints(); // 添え字を指定して追加
}
配列の要素が $N$ 行あるので、for 文を $N$ 回まわします。配列を一つずつ取得して、空配列に追加していきます。配列として標準入力を取得したいため、配列の分解は使用しません。
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}$
// パターン1:Aが整数型の場合
[$n] = ints();
$k = [];
$a = [];
for ($i = 0; $i < $n; ++$i) {
$line = ints();
$k[] = array_shift($line); // 先頭の要素を取得して、配列からは削除
$a[] = $line;
}
// パターン2: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」を使用
}
パターン1は二次元配列の要素が 整数型 の場合です。列数 $k_i$ と型が一致するため、ints()
で標準入力を取得します。
パターン2は二次元配列の要素が 文字列型 または 小数型 の場合です。列数 $k_i$ と型が異なるため、strings()
で標準入力を取得したあと、キャスト または array_map()
を使用して適切な型に変換します。
2.7 クエリ問題
$Q$
$query_1$
$query_2$
$...$
$query_Q$ただし、$query_i$ は $i$ 個目のクエリを表しており、次の形式のいずれかで与えられる。
$1 k x$
$2 k$
[$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 を例として挙げたものです。(標準入力を一部改変しています。)
クエリ問題は種類によって標準入力の形式が異なるため厄介です。対処法として、クエリを配列として受け取り、クエリを使用するときに適切な形式で変数に代入するのが良いかと思います。
2.8 マルチテストケース問題
$test_i$ は $i$ 番目のテストケースを意味する。
$T$
$test_1$
$test_2$
$...$
$test_T$各テストケースは以下の形式で与えられる。
$N$
$A_1 A_2 ... A_N$
// パターン1:solve()関数内でテストケースの標準入力を受け取る
function solve()
{
[$n] = ints();
$a = ints();
}
[$t] = ints();
for ($i = 0; $i < $t; ++$i) {
solve();
}
// パターン2:for文内でテストケースの標準入力を受け取る
[$t] = ints();
for ($i = 0; $i < $t; ++$i) {
[$n] = ints();
$a = ints();
}
「各テストケースは以下の形式で与えられる」の部分は問題によって異なります。上記は B - Multi Test Cases を例として挙げたものです。
テストケースが $T$ 個あるため、for 文を $T$ 回まわします。
パターン1はテストケースの標準入力の受け取りを関数に切り出す方法です。関数内では一つのテストケースのみに着目できるため、マルチテストケースではない普通の問題と同じようにコードが書けます。
パターン2は for 文内でテストケースの標準入力を受け取る方法です。
- B - Multi Test Cases
- E - (∀x∀) ※難易度高めです
2.9 一行に複数の型が存在
$N$
$A_1 S_1$
$A_2 S_2$
$...$
$A_N S_N$
※ $A_i$ は整数、$S_i$ は文字列
// パターン1:array_map()関数で型変換
[$n] = ints();
$a = [];
$s = [];
for ($i = 0; $i < $n; ++$i) {
[$a[], $s[]] = strings();
}
$a = array_map('intval', $a); // 整数型に変換
// パターン2:キャストで型変換
[$n] = ints();
$a = [];
$s = [];
for ($i = 0; $i < $n; ++$i) {
[$a[], $s[]] = strings();
$a[$i] = (int)$a[$i]; // 整数型に変換
}
上記は B - Piano 3 を例として挙げたものです。
一行に複数の型が存在する場合は、strings()
で文字列として受け取り、 必要に応じて型変換をしてください。
3. 標準出力
3.1 単体の値を出力する
output(123);
output('abc');
output(3.14);
123
abc
3.14
3.2 複数の値を空白区切りで出力する
output(123, 'abc', 3.14);
123 abc 3.14
3.3 配列の値をN行で出力する
$n = 3;
$a = [10, 20, 30];
for ($i = 0; $i < $n; ++$i) {
output($a[$i]);
}
10
20
30
3.4 配列の値を1行で出力する
$a = [10, 20, 30];
output(...$a);
10 20 30
4. まとめ
- 整数型、文字列型、小数型として値を受け取るときは 配列の分解 を使用する
- 配列として値を受け取るときは配列の分解を使用しない
- ただし、配列の標準入力が $N$ 行に分かれている場合は、配列の分解で要素を一つずつ受け取って空配列に追加していく
- 一行に複数の型が存在する場合は、
strings()
で文字列型として受け取り、あとから適切な型に変換する- 単体の値を型変換する方法:キャスト
- 整数型:
(int)
- 文字列型:
(string)
※最初に文字列型として受け取るため、使用することはない - 小数型:
(double)
- 整数型:
- 配列の値を型変換する方法:
array_map()
関数- 整数型:
array_map('intval', $array)
- 文字列型:
array_map('strval', $array)
※最初に文字列型として受け取るため、使用することはない - 小数型:
array_map('doubleval', $array)
- 整数型:
- 単体の値を型変換する方法:キャスト