ログインしてさらにmixiを楽しもう

コメントを投稿して情報交換!
更新通知を受け取って、最新情報をゲット!

Lispで書くテキスト処理コミュのCommon Lispでも遊んでみる?(笑)

  • mixiチェック
  • このエントリーをはてなブックマークに追加
きっと何の断りも無しにLispと言った場合はCommon Lispを指すのだと思います。Emacs上でCommon Lispを楽しむ場合は、C-u M-x run-lispとタイプして、clisp(これはCygwinの場合?)を指定すると画像のようにEmacsをフロントエンドとしてCommon Lispを使用することが出来ます。gclがインストールされていれば、それを使ったほうが良いと思われます。トンでもなく大きな自然数の計算が出来て面白いですよ。

http://nobulign.way-nifty.com/Lisp/WS000105.JPG

コメント(9)

Common Lispで遊んでみました。というか初めてxyzzyでコマンドを書きました。
例によってCSVの処理です。

最初に気付いたのは・・・

(let ((list nil) (string "A"))
 (dotimes (count 3)
  (setq list (cons string list)))
 list)

の実行結果が、xyzzyの場合は(#1="A" #1# #1#)のように表示されることです。
emacsでは("A" "A" "A")と表示されます。内部のオブジェクトの扱いは分かりま
せんが、少なくともxyzzyの方は最初の要素と続く要素が同一のオブジェクトで
ある?ことを表現しようとしているように見えます。

(apply 'concatenate 'string (let ((list nil) (string "A"))
               (dotimes (count 3)
                (setq list (cons string list)))
               list)) ; xyzzy

(apply 'concat (let ((list nil) (string "A"))
        (dotimes (count 3)
         (setq list (cons string list)))
        list)) ; emacs

上記の2つはどちらも"AAA"が返ります。効率の良いコードを書きたい人はxyzzy
の出力形式を歓迎するでしょうし、私のようにイイ加減な人は分かりやすい方が
良いと思うのでしょうね。

その他、関数やマクロのパラメータが微妙に違うのは既知のとおりですが・・・

(split-string ",,," ",") ; xyzyy
nil

(split-string ",,," ",") ; emacs
("" "" "" "")

この2つの違いは何とかならないものか?という気がします。CSVの処理において
は、どっちが正しいかは別として、emacsの出力じゃないと困ると思いませんか?
最初はレコードごとにフィールドの位置や数が合わないのはなぜだろうと、すご
く悩みました。

まあ、みなさんもお気をつけください。
・・・ということで、誰も突っ込まないようなので、自分用にへなちょこ関数を
書いてみました。少なくともCSVの処理くらいには使えそうな気がしています。
ろくに試験していないので、もし使う場合は十分注意してください。

また、こんなアホな関数を書かなくても、こんな方法があるよ?っていうご意見
もお待ちしております。


(defun mylib-split-string (string separator)
 (save-window-excursion
  (prog1 (with-output-to-temp-buffer ("*TEMP*")
       (delete-region (point-min) (point-max))
       (insert string)
       (goto-char (point-min))
       (replace-string separator "\n")
       (goto-char (point-min))
       (let ((list nil))
        (while (not (eobp))
         (setq list
            (cons (buffer-substring (point)
                        (progn
                         (end-of-line)
                         (point)))
               list))
         (forward-line))
        (and (string-match (concatenate 'string separator "$") string)
          (setq list
             (cons (buffer-substring (point)
                         (progn
                          (end-of-line)
                          (point)))
                list)))
        (reverse list)))
   (kill-buffer "*TEMP*"))))
> xyzzyの場合は(#1="A" #1# #1#)

xyzzyは使っていませんのではずしているかもしれませんが、*print-circle*スペシャル変数が非nilになっているということは無いでしょうか?
sbclの場合、

CL-USER> (setf *print-circle* t)
T
CL-USER> (let ((list nil) (string "A"))
(dotimes (count 3)
(setq list (cons string list)))
list)
(#1="A" #1# #1#)
CL-USER> (setf *print-circle* nil)
NIL
CL-USER> (let ((list nil) (string "A"))
(dotimes (count 3)
(setq list (cons string list)))
list)
("A" "A" "A")
CL-USER>

となりますが、xyzzyはどうでしょうか?
ほげたさん

xyzzyでは、*print-circle*はnilになっています。

ちなみにemacsの*scratch*バッファのLisp Interactionメニューにある「
Instrument Function for Debugging」を使って、Lisp式をステップ実行すると、
都度ミニバッファに評価結果が表示されますが、その表示は("A" "A" "A")では
なく、(#1="A" #1# #1#)になるのです。

ちょっとディープな領域なのかもしれません(^^;
書き忘れました・・・

emacsにもprint-circle変数があり、これをNon-nilにすると(#1="A" #1# #1#)の
表示になるようです。この表示が何を意味するのかは分かりません。ヘルプが表
示する、この変数の説明は下記のとおりです。再帰構造のPrint表記には#N= and
#N# syntaxを使用する??・・・謎です(^^;

*Non-nil means print recursive structures using #N= and #N# syntax.
実は今回xyzzyでコマンドを書いたのには理由があって・・・処理したいCSVファ
イルは項目がたくさんあって、しかもレコード数が多く、ファイルサイズが5.5M
バイトもあるのです。これをEmacs(Meadow)で普通に読み込むと、文字化けして
しまって処理出来ません。ところがxyzzyだと、これをすんなり読み込んで、し
かも処理がわりと速いのです。

ちなみに、このCSVをgawkにかけて、処理に不要な項目を間引こうとしても、な
ぜかセパレータを認識してくれません。ひょっとしたら処理可能な項目数をオー
バーしているのかもしれません。

それでやむなく慣れないCommon Lispでコマンドを書き、そこそこ速いので喜ん
でいたのですが、xyzzyってEmacsのようなバッチモード(-batchオプションを付
けて起動する)がないので、タスクスケジューラから起動すると動いてくれませ
ん。もし、やり方を知っている方は教えてください。

ただ、Meadowが文字化けする問題は、ファイルを読み込んだ後に
revert-buffer-with-coding-systemで無理やり文字コードを変更することが出来
ることは分かりました。でも、これをやると「こんなにバッファを変更したら
Undo出来ないよ?」という?ワーニングが出しまい、こんなのバッチモードで処
理中に出されても困ってしまいます。このワーニングをサプレスする方法はある
んでしょうけど調べきれていません。

今は仕方なく、sedの1行出力(sed -n -e '1p')をテンポラリバッファに書き込ん
で、行番号を変更しながらコツコツと処理する方法を試しているところです。1
行ずつなら文字化けはしないし、large fileやUndoのワーニングも出ないからで
す。

xyzzyにバッチモードがない理由は・・・やっぱりWindowsのフレームワークに乗
っかって処理しているからでしょうか?バッファとかのクラスが画面関係のクラ
スに含まれているとするとあり得るような気がします。検索しても、xyzzyから
バッチファイルを実行する方法しか見つからなくて、イライラしてしまいました。
もう、どうにでもなれモードに入りつつありますが・・・

sedの1行プリント版を試したところ、edebugでトレースするとうまく動くように
見えますが、バッチモードで実行すると(バッチファイルから起動すると)ダメみ
たいです。何がダメかというと、CSVレコードはこれでおしまいの判定をsedの出
力が空文字かどうかで判定していて、バッチから起動するとその判定が出来ない
ようなのです。原因として、sedをshell-commandから起動していること、文字コ
ードをきちんと指定出来ていないこと(これはsed以前の問題?)などを思いつき
ましたが・・・。

(save-excursion
 (shell-command "sed -n -e 100p foo.csv" t))

(call-process "/bin/sed" nil t nil "-n" "-e" "100p" "foo.csv")

どちらでやっても、戻り値はプリント対象行の存在を判定可能なものではないし、
実行後にカレントバッファで(= (point-min) (point-max))の判定が効かないよ
うなので、あんまり変わらない気がしますが・・・。

みんな大好きなSchemeだとうまくいくのかな?

あと、上記のLisp式に反するかもしれませんが、実はこれを動かしたい環境が
Windows 2008 Serverなのです。Linux環境だったら、splitでemacsの負担になら
ない程度にファイルを分割して処理することも考えられるのに、DOSプロンプト
ではどうしようもなく・・・でも、DOSプロンプト用のsplitがあったりして?
今のところ、Cygwinをインストールする気はありません(というか動くの?)。

という状況です(^^;
ええと、一応問題は解決しました。

結局はファイル分割でもなく、sedの1行出力でもなく、Meadowのファイル読み込
み処理を自分なりに工夫してOKになりました。考え方はこうです。

(1) find-file-noselectでCSVファイルを読み込む。
(2) Undoを無効にする。
(3) revert-buffer-with-coding-systemの処理をパクる。

revert-buffer-with-coding-systemというコマンドでは、coding-systemを入力
させて、それとカレントバッファのbuffer-file-coding-systemをマージして得
たcoding-systemをcoding-system-for-readに設定してrevert-bufferを呼び出
す・・・というものです。

他にもっとエレガントな方法があれば教えてください。

結果的に、Common Lispではホントに遊んだだけになってしまいました(^^;
もうずっと前に判明したことですが・・・

(let ((coding-system-for-read 'japanese-shift-jis-dos))
 (insert-file-contents ファイル))

とすれば良かったのでした。
Undoを無効にしたり、revert-bufferする必要はありません。
慌てずに、いくつかのelispファイルを読めば分かることでした。

ログインすると、みんなのコメントがもっと見れるよ

mixiユーザー
ログインしてコメントしよう!

Lispで書くテキスト処理 更新情報

Lispで書くテキスト処理のメンバーはこんなコミュニティにも参加しています

星印の数は、共通して参加しているメンバーが多いほど増えます。

人気コミュニティランキング