2016年10月29日土曜日

「Land of Lisp」7章の "dot->png"関数をSBCLで書く

「Land of Lisp」を読み始めている。

第7章まで読み終わったのだが、この章の最後の方(P.115)に、DOTファイルをPNGファイルに変換する dot->png という関数が紹介されている。この本ではLISP処理系にはCLISPを使っているので、この関数の中でシェルを呼び出す部分もCLISP独自の関数を使っている。

私はLISP処理系にSBCLを使っているので、以下にSBCLでdot->png関数を書いてシェルを呼び出す方法を備忘録として書いておく。

(defun dot->png (fname thunk)
  (with-open-file (*standard-output*
                   fname
                   :direction :output
                   :if-exist :supersede)
    (funcall thunk))
  (sb-ext:run-program "/bin/sh"
                      (list "-c"
                          (concatenate 'string "dot -Tpng -O "
                                       fname))))

sb-ext:run-program の後に、呼び出したいプログラムへのパスを文字列にしたものと、そのプログラムに渡すコマンドを文字列にしたリストを与えればいいのだが、-cオプションも書かなければいけないことがわからなくて、かなり悩んでしまった。シェルスクリプトは全然知らない。

-c string

-c オプションが指定されると、コマンドが string から読み込まれます。 string の後に引き数があれば、これらは 位置パラメータ (positional parameter: $0 から始まるパラメータ) に代入されます。
(出典:Man page of BASH

-cのようなオプションはコマンドとは別の文字列として渡す必要がある。

(list (concatenate 'string "-c dot -Tpng -O " fname))

などのようにオプションをコマンドの文字列に含めてしまうとうまくいかない。

また、このdot->png関数には誤植ではないが冗長と思われる箇所があったので指摘しておく。

本書のP.115では

(defun dot->png (fname thunk)
  (with-open-file (*standard-output*
                   (concatenate 'string fname ".dot") ;; (1)

  (以下省略)

となっているが、(1)の部分は、

(defun dot->png (fname thunk)
  (with-open-file (*standard-output*
                   fname ;; (1)'
(以下省略)

と、(1)’のようにfnameだけでよいと思う。

graph->pngを呼び出す際に、

(graph->png "wizard.dot" *wizard-nodes* *wizard-edges*)

というようにwizard.dotと拡張子まで含めて引数として渡しているため、(1)のコードでは出力されるDOTファイル名がwizard.dot.dotとなってしまう。

2 件のコメント:

  1. ちょうどここで悩んでいました。記事にしてくださりありがとうございます!

    返信削除
    返信
    1. お役に立ててよかったです(^-^)

      削除