プログラマとプロマネのあいだ

プログラマもやるし、プロマネもやるし、たまに似非アーキとか営業っぽいこともやるITエンジニアがスキルアップの話を中心に日常を綴るブログです。

「Ansi Common Lisp」7章練習問題

1. ファイル名を引数として、ファイルの各行を構成するストリングをリストとして返す関数を定義せよ。

(defun file-to-str-list (file)
  (let ((result nil))
    (with-open-file (str file :direction :input)
      (do ((line (read-line str nil 'eof)
                 (read-line str nil 'eof)))
         ((eql line 'eof))
        (setf result (cons line result))))
    result))

(file-to-str-list (make-pathname :name "ex7-1.l"))

結果はこう。

*
FILE-TO-STR-LIST
*
("(file-to-str-list (make-pathname :name \"ex7-1.l\"))" "" "    result))"
 "        (setf result (cons line result))))" "         ((eql line 'eof))"
 "                 (read-line str nil 'eof)))"
 "      (do ((line (read-line str nil 'eof)"
 "    (with-open-file (str file :direction :input)" "  (let ((result nil))"
 "(defun file-to-str-list (file)")
* 

2. ファイル名を引数として、ファイル中の式をリストとして返す関数を定義せよ。

(defun file-to-list (filename)
  (let ((path (make-pathname :name filename)))
    (let ((in (open path :direction :input)))
      (read in))))

(file-to-list "ex7-1.l")

readに食わせるだけ。めちゃくちゃ簡単。
結果はこう。

*
FILE-TO-LIST
*
(DEFUN FILE-TO-STR-LIST (FILE)
  (LET ((RESULT NIL))
    (WITH-OPEN-FILE (STR FILE :DIRECTION :INPUT)
      (DO ((LINE (READ-LINE STR NIL 'EOF) (READ-LINE STR NIL 'EOF)))
          ((EQL LINE 'EOF))
        (SETF RESULT (CONS LINE RESULT))))
    RESULT))
* 

3. コメントを%で示すようなテキストファイルのフォーマットがあるとする。この文字が出現した場合、その行のそれ以降の部分は全て無視される。2つのファイル名を引数とし、第2のファイルに第1のファイルのコメントを除外したコピーを作成する関数を定義せよ。

(defun remove-percent-comment (infile outfile)
  (with-open-file (in (make-pathname :name infile) :direction :input)
    (with-open-file (out (make-pathname :name outfile) :direction :output
                                                       :if-exists :supersede)
      (do ((line (read-line in nil 'eof)
                 (read-line in nil 'eof)))
         ((eql line 'eof))
        (let ((percent-position (position #\% line)))
          (if percent-position
            (format out "~A~%" (subseq line 0 percent-position))
            (format out "~A~%" line)))))))

(remove-percent-comment "infile" "outfile")

with-open-fileマクロは普通にネストも出来るんですね。
結果はこう。

taka@taka-desktop:~/lisp$ more infile
abc % def
%ghi
%%
jklmn
opqrs%
*
REMOVE-PERCENT-COMMENT
*
NIL
*
taka@taka-desktop:~/lisp$ more outfile
abc


jklmn
opqrs
taka@taka-desktop:~/lisp$

4. 浮動小数点数の2次元配列を引数として、桁をそろえて表示する関数を定義せよ。各要素は、小数点以下2桁を表示し、フィールドの幅は10字分とする。(全てこれで収まるものと仮定せよ。) 関数array-dimensions(配列を引数として、各次元の長さ(整数)をリストにして返す)が必要となるだろう。

(defun print-2d-float-array (arr2d)
  (let ((dimensions (array-dimensions arr2d)))
    (do ((i 0 (+ 1 i)))
      ((eql i (car dimensions)))
      (do ((j 0 (+ 1 j)))
        ((eql j (car (cdr dimensions))))
        (format t "~10,2,0,'*,' F" (aref arr2d i j))
        (when (eql j (- (car dimensions) 1))
          (format t "~%"))))))

(print-2d-float-array #2a((1.234 2.34) (3.4567 4)))

結果はこう。

*
PRINT-2D-FLOAT-ARRAY
*       1.23      2.34
      3.46      4.00
NIL
* 

なんかプロンプトの*でズレてますが、まあ一応出来てますね。

5. stream-substを改変して、パタン中にワイルドカードが使えるようにせよ。oldに+文字が出現したら、その部分は任意の入力文字とマッチするものとする。

元のソース(図7.1と図7.2)を写すのが大変だった。。
長いのでdiffだけ。
ちなみにstream-substは、置換前の文字列、置換後の文字列、入力ファイル、出力ファイルをパラメータとして、入力ファイルの置換結果を出力ファイルに書くという関数です。

57c57,58
<       (cond ((char= c (char old pos))
---
>       (cond ((or (char= c (char old pos))
>                 (char= #\+ (char old pos)))
78c79
< (file-subst "abc" "def" "infile" "outfile")
---
> (file-subst "ab+" "def" "infile" "outfile")

要は、マッチングしているところで、'+'もorで判定してあげればいいだけのことでした。

  1. 拡張正規表現で1文字以上にマッチとか、深く考えたらいけません。

この問題は、+は?みたいな意味で捕らえます。

結果はこう。

taka@taka-desktop:~/lisp$ more infile
abc % def
%ghi
%%
jklmn
opqrs%
taka@taka-desktop:~/lisp$ more outfile
def % def
%ghi
%%
jklmn
opqrs%
taka@taka-desktop:~/lisp$

6. stream-substを改変して、パタンが任意の数字とマッチする要素、任意のアルファベット・数字とマッチする要素、任意の文字とマッチする要素を含むようにせよ。パタンは任意の特定入力文字とマッチする必要もある。(ヒント: oldはストリングでなくなることもある。)

多分題意は、

  • 任意の数字とマッチする要素 → #
  • 任意のアルファベット・数字とマッチする要素 → $
  • 任意の文字とマッチする要素 → +

とかって定義して、それぞれマッチするようにしなさいってことだと思う。
ヒントのところは、oldに123とか文字列じゃないのが渡ってくるっていう意味かな?


どうやって数字とマッチとか、アルファベット・数字とマッチとかテストするのだろう。。
仕方がないので(?)答え見た。
http://www.shido.info/lisp/pacl2.html#io
alpha-char-pとか、digit-char-pとかあるんですね。
て、見直したら以前に出てた。完全に見逃してるなあ。
回答例は、%aとかマッチ文字が2文字になっててカッコイイ。つかすごいなあ。

方向性は5と同じなので、飛ばし。