read-line EOF icon Common Lisp read-line EOF handling icon ( ) 【Common Lisp】
(read-line *standard-input* nil
“”) はどんなときに使う?
(EOF終端)

  • 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-ptEOF でエラーを出すかどうか
eof-valuenilEOF のときに返す値

つまり (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に達していなくても終了してしまいます。

また、入力が足りなかった場合でも "" として処理が流れるので、バグがあっても気づくのが遅くなります。