標準入力ジャック *in* jack

*in* (System.io)からの入力を前提としてしまった関数(*in*がべた書きされている)があり、動作を見るために入力データをファイルから読み込みたい。綺麗なやり方は引数で入力先を渡してやることだが、関数のあちらこちらで入力を読み込んでいてそれを修正するのは面倒くさい、一時しのぎでいいから楽がしたい。この問題は、、進研ゼミでやった! binding でダイナミックスコープを利用する汚いプログラムが使えそうだ。

  • 結果: これでok.
(binding [*in* (java.io.BufferedReader. 
                    (java.io.FileReader. "maps/empty-room.txt"))]
   (initialize)
   (.close *in*))
  • 過程:

まず、*in*は System.in らしいと言うことで、javadoc から InputStream の File版 FileInputStream に書き換えればいいんだろうと推測。

user=> (binding [*in* (java.io.FileInputStream. "maps/empty-room.txt")] (initialize))
FATAL ERROR: Unknown exeption: java.lang.ClassCastException: 
java.io.FileInputStream cannot be cast to java.io.BufferedReader

Stream じゃなくて Reader だったみたい

user=> (doc *in*)
-------------------------
clojure.core/*in*
nil
  A java.io.Reader object representing standard input for read operations.

  Defaults to System/in, wrapped in a LineNumberingPushbackReader
nil
user=> *in*
#<LineNumberingPushbackReader clojure.lang.LineNumberingPushbackReader@30d1e7c2>

それじゃあ Reader の File 版 FileReader を渡そう。

user>  (binding [*in* (java.io.FileReader. "maps/empty-room.txt")]
         (initialize)
         (.close *in*))
FATAL ERROR: Unknown exeption: java.lang.ClassCastException:
 java.io.FileReader cannot be cast to java.io.BufferedReader

あ、BufferedReaderじゃないと受け取ってくれないんだ。と言うことでBufferedReaderかませて成功。汚いプログラムでした。

あと、read がオプショナルに stream を渡せるんだったら、read-line も *in* 固定じゃなくて stream 渡せればいいのにと思った。

user> (doc read)
-------------------------
clojure.core/read
([] [stream] [stream eof-error? eof-value] 
[stream eof-error? eof-value recursive?])
  Reads the next object from stream, which must be an instance of 
  java.io.PushbackReader or some derivee.  stream defaults to the 
  current value of *in* . 
nil
user> (doc read-line)
-------------------------
clojure.core/read-line
([]) 
  Reads the next line from stream that is the current value of *in* . 
nil