【CommonLisp】プログラミング問題用に標準入力を考える

Lispの標準入力はreadとread-lineがあるのですが、
アルゴリズムの練習問題を解くのに両方共対応できなかった。


例えば、「1 2 3 4 5」のように標準入力で受け取って、何かしらの処理をする問題があるとする。
Rubyとかだと標準入力で文字列として受け取って配列に詰めたりする。

で、Lispでも同じように扱おうと思ったのだが苦戦した(´・ω・`)



例として、『標準入力で受け取った空白区切りの数字をそれぞれ倍にしなさい』という問題があったとする。
入力例 1 2 3 4 5 → 2 4 6 8 10 を出力する。

このとき、
1 2 3 4 5という入力をちゃんと処理できなくてはならないのと、
1 2 3 4 5をlisp的に最適な形で受け取る必要がある。
つまり、文字列でうけとるよりもリストとして受け取りたいということ。

readで試す

;受け取った値をそのまま出力
(print (read))
> '(1 2 3 4 5)
'(1 2 3 4 5)

read関数の場合は入力はLisp構造で入力してもらわないといけない。
1 2 3 4 5と入力された場合は、正しく受け取ることができない。


read-lineで試す

(print (read-line))
> 1 2 3 4 5
"1 2 3 4 5"

read-line関数の場合は期待通り1 2 3 4 5のように入力されても問題ありません。
ただ、受け取る形は文字列です。
Lispの場合、文字列として受け取っても結構困るんですよね。

(car ("1 2 3 4 5"))

文字列なのでcarなんかを使おうとしてもエラーです。リストじゃないよって怒られます。



で、解決策としてとりあえず。。

;標準入力関数作ったよ☆
(defun read-stdin ()
  (read-from-string (cocatenate 'string "(" (read-line) ")")))

(read-stdin)
1 2 3 4 5
(1 2 3 4 5)

入力された値をそのままLispのデータとして受け取れるようにしました。

(car (read-stdin))

もちろん先頭の値だけ取ることができます。


自作したread-stdin関数は何してるかと言うと、
concate関数を使って文字列を結合しています。受け取った"1 2 3 4 5"を"("と")"で結合して"(1 2 3 4 5)"にしてます。
ただ、この段階ではまだ文字列なのでデータ型ではないです。
read-from-string関数を使うと、引数の文字列を読み込んでLisp構文に変換してくれます。
結果として受け取った入力をデータ型として扱うことができます。やったね!


最後に、例題解いときます。

;標準入力関数
(defun read-stdin ()
  (read-from-string (concatenate 'string "(" (read-line) ")")))

(princ (mapcar (lambda (n) (* n 2)) (read-stdin)))
> 1 2 3 4 5
2 4 6 8 10