read-lineは引数を省略できるが、第2・第3引数で EOF 時の挙動を制御できる(read-line)は EOF でエラーになり、(read-line *standard-input* nil "")は""を返して続行する- 入力行数が確定しているなら
(read-line)で十分。行数が不定のときに第2・第3引数が意味を持つ
1. read-lineに見慣れない引数があった
ほかの人の Common Lisp コードを読んでいたら、文字列の入力でこんな書き方が出てきました。
(read-line *standard-input* nil "")Code language: Lisp (lisp)
いつも (read-line) と書いていたので、この 3つの引数が必要なのか、理由が気になりました。
2. read-line とEOFの処理
両方とも、通常の読み取り中は、どちらも同じように動きます。
差が出るのは、ストリームを読み切った後にもう一度呼ばれたときだけです。
;; EOF でエラーになる
(read-line)
;; EOF で "" を返す(エラーにならない)
(read-line *standard-input* nil "")Code language: Lisp (lisp)
2.1. read-lineの構文
read-line の完全な形はこうです。
(read-line &optional stream eof-error-p eof-value recursive-p)Code language: Lisp (lisp)
&optionalで、追加の引数をセットでき、意味は次のとおりです。
| 引数 | 省略時のデフォルト | 意味 |
|---|---|---|
stream | *standard-input* | 読み取り元のストリーム |
eof-error-p | t | EOF でエラーを出すかどうか |
eof-value | nil | EOF のときに返す値 |
つまり (read-line) は、(read-line *standard-input* t nil) と同じです。(read-line *standard-input* nil "")との違いは、EOF(入力の終端)に達したときの挙動ということになります。
3. 判断基準は「入力行数が確定しているか」
read-lineのEOF処理としては、3つのパターンが一般的です。
(read-line *standard-input* t nil)
(read-line *standard-input* nil nil)
(read-line *standard-input* nil "")Code language: Lisp (lisp)
AtCoder などの競技プログラミングでは、入力形式が仕様で決まっています。
読むべき行数はあらかじめわかっているなら、EOF を超えて読もうとすることはないので、(read-line) で十分です。
(loop for i from 0 below h
for line = (read-line)
...)Code language: Lisp (lisp)
3.1. 対話的な入力プログラムではEOFチェックが必要
一方、入力の終わりがわからない場合は、EOF を検出しながら読む必要があります。
入力の終わりがわからない場合、EOF を検出しながら読む必要があります。
eof-value にnil を使う場合はこうなります。
(loop for line = (read-line *standard-input* nil nil)
while line
do (process line))Code language: Lisp (lisp)
EOF に達すると nil が返るので、それを終了条件にできます。
ファイルを末尾まで読み切るユーティリティや、対話的な入力を受け付けるプログラムでは、こちらの形が適しています。
3.2. eof-valueに "" を使うメリット・デメリット
eof-value を "" にすると、戻り値が常に文字列になります。
この書き方は、対話的なプログラムで「空行で終了」という設計が明確なときに使います。
(loop for line = (read-line *standard-input* nil "")
until (string= line "")
do (process line))Code language: Lisp (lisp)
ユーザーの入力とファイル入力を両方とも受け取るプログラムでは、ユーザーがEOFを入力するのは大変です。
この方法なら、空行とEOFを同じ処理にできます。
ただし、デメリットもあり、入力に空行が含まれていれば、それが終端なのかわからなくなります。
つまり、ファイルの途中に空行があれば、EOFに達していなくても終了してしまいます。
また、入力が足りなかった場合でも "" として処理が流れるので、バグがあっても気づくのが遅くなります。