2008年12月26日金曜日

'fooと#'fooの違いは環境の違い

QUOTEとFUNCTIONを使い分ける:もう少し詳しい説明 -- 逆引きCommonLisp

をぼんやり見ていて、そういえば FUNCTION と COERCE では環境が違うってうちの CLISP が言ってたのを思い出した。

(funcall '(lambda (x) (1+ x)) 1)
;; => Error
;; *** - FUNCALL: argument (LAMBDA (X) (1+ X)) is not a function.
;;       To get a function in the current environment, write (FUNCTION ...).
;;       To get a function in the global environment, write (COERCE '... 'FUNCTION).

CLHS にもそれらしい記述があったのでメモ。

CLHS: Function COERCE
If the result-type is function, and object is any function name that is fbound but that is globally defined neither as a macro name nor as a special operator, then the result is the functional value of object. If the result-type is function, and object is a lambda expression, then the result is a closure of object in the null lexical environment.

= objectがマクロ、スペシャル・オペレータ以外でグローバルに定義されたfboundな関数名を持っているなら、そのobjectの関数値を返す。 objectがラムダ式なら、空レキシカル環境なobjectのクロージャを返す。

CLHS: Special Operator FUNCTION
The value of function is the functional value of name in the current lexical environment.

= functionは現在のレキシカルな環境を持ったnameの関数値を返す。

この環境を引数に渡せる関数、マクロはいくつかある。 ラムダリストキーワードに &environment を持つものがそれで、例えば setf とか push とか。

(arglist 'setf)
;=> (&WHOLE SYSTEM::WHOLE-FORM &REST SYSTEM::ARGS &ENVIRONMENT SYSTEM::ENV)

macroexpand も環境を引数に渡せるが、こちらはなぜか &optional になっている。

(arglist 'macroexpand)
;=> (FORM &OPTIONAL ENV)

2008年12月19日金曜日

あと一週間

万能プレーヤーである VLC media player の起動アイコンがクリスマス仕様になっている事に気づいた。

Google のトップページが時期によって模様替えされるのは何度か見たけれど、こういう遊びをするソフトウェアって少ないんじゃないかな。

2008年12月18日木曜日

おおえまくすよ マクロをつかわないとは なさけない

(require 'cl) は悪なのか?の続き。

気が向いたので軽く調べてみた。

---

GNU Emacs のマニュアルには以下のような注意書きがある。

Common Lisp Extensions Overview
Please note: the CL functions are not standard parts of the Emacs Lisp name space, so it is legitimate for users to define them with other, conflicting meanings. To avoid conflicting with those user activities, we have a policy that packages installed in Emacs must not load CL at run time. (It is ok for them to load CL at compile time only, with eval-when-compile, and use the macros it provides.) If you are writing packages that you plan to distribute and invite widespread use for, you might want to observe the same rule.

どうやら Emacs 実行時の cl パッケージ読み込みは禁止されていて、コンパイル時のマクロ展開に限って読み込みを許可しているらしい。つまり (require 'cl) ではなくて (eval-when-compile (require 'cl)) を使いなさいってことか。

コンパイル時にマクロ展開をしなければならないのは Common Lisp でも同じだから、この点は納得。

the CL functions are not standard parts of the Emacs Lisp name space

これは「名前には接頭辞を付けろ」という elisp のコーディング規則に反しているから関数上書きしないように気をつけてね、そんな意味でしょうか。

んー、名前空間の話は cl パッケージを読み込んだ時点でユーザは承知していると思うんだが。

ありがたいことに elisp のマクロは CL のマクロと(ほぼ)同等の機能があるのだから、マクロによる抽象化によってソースコードが短く、より簡素になるなら利用しない手はない。見やすいコードとそうでないコード、どっちを書きたい?と質問するようなもの。

…というのが自分の意見です。

所詮 .emacs 位しか書いたことのない学生の意見ですから反論は認めます。mew の作者さんのように何かしらの elisp パッケージを作成した方は、きっとこの cl パッケージのおかげで互換性等々に苦労されたんだと思いますし。

まとめ、あるいは教訓:

  • cl パッケージを利用するファイルは、必ずコンパイルして実行時にそれに依存しない形にすること。(eval-when-compile (require 'cl)) 用法用量を守って正しく使いましょう。
  • マニュアルはなるべく最新のものを参照すること。OSS のマニュアルの邦訳がメンテナンスされないのはよくある話。

おまけ:

「マクロではなく cl.el の関数群を使いたいときはどうすれば?」と質問された時の Stallman 氏の返事

Installed Lisp packages must not use those functions.

2008年12月15日月曜日

[Emacs] newLISP 環境を構築してみたい

[追記@2009-04-20T03:29:05]
もう少し拡張したものを置いておきます。
http://github.com/kosh04/newlisp-files/tree/master


Emacs から newLISP のプロセスを操作する関数群を書いたのでちょっと醸してみます。

もともと xyzzylisp 用に作ったんだけど、やっぱり資産がある elisp の方が短くできてしまったので。

使い方:

  1. 以下の URL のコードを newlisp.el で保存して eval-buffer
    http://paste.lisp.org/display/72178
  2. 必要に応じて、以下の変数を書き換え。
    • newlisp バイナリのパス名: *newlisp-command*
    • プロセス間文字コード: *newlisp-process-coding-system*
    Windows インストーラから newLISP をインストールした場合は、環境変数 NEWLISPDIR が設定されているはずなのでパス名はそのままで大丈夫だと思います。 newlisp バイナリには utf-8 が使える版と使えない版があるので、ここではマルチバイト文字を扱える utf-8 版を推奨します。 →[xyzzy] newLISP 環境を構築してみたい
  3. 後のS式操作はカンでお願いします。スクラッチバッファで遊んだことがある人ならたぶん分かるでしょう。 主に使うのは newlisp-eval-last-sexpnewlisp-show-repl くらいでしょうか。

残念ながらハイライトなどのリッチな機能はありません。

あ...それと (require 'cl) が必要かもしれないです。


Emacs(VineLinux4.2) だとなぜか評価のタイミングがずれました。
(一回目の newlisp-eval-last-sexp では何も表示されないで、二回目でまとめて出力されてしまう)
NTEmacs では問題なく動いたので、 Emacs 側の問題?

(require 'cl) は悪なのか?

自分の中で Emacs Lisp の CL パッケージの評判があまりよろしくない印象なんだが、はっきりした理由を聞いたことがないなあ。

気が向いたらまとめのページが無いか詮索してみよう。

詮索した。

elisp にもラベル記法があったのか

(let ((print-circle t)
      (lst '(7 8 3 9 4 0 1 6 5 2)))
  (princ (list (sort lst #'<) lst))
  (terpri))
;-> ((0 1 2 3 4 5 6 . #1=(7 8 9)) #1#)
;=> t

((lambda (x)
   ((lambda (y1)
      ((lambda (&optional y2)
         ((lambda (z)
            (list x y1 y2 z))
          (car (cdr #1=(cdr #2='(1 (2) 3))))))
       (car (cdr #3=(car #1#)))))
    (car #3#)))
 (car #2#))
;=> (1 2 nil 3)

;; 上のコードは少し古い destructuring-bind のマクロ展開 (CL)
(macroexpand '(destructuring-bind (x (y1 &optional y2) z) '(1 (2) 3)
                (list x y1 y2 z)))

Emacs Lisp はかなり Common Lisp に近い仕様なんだなと改めて思った。
# もちろん動的スコープだとかクロージャが無いとかツッコミはあるけど :-)

しかしこの書き方 (#n#) の名称なんだっけ...


CLtL2, CLHS をパッと見たけど 「#n# 構文 (#n# syntax)」としか書いてなかった。

あと、影響を受けたのは CL より Maclisp 方言か。歴史知らなさ過ぎかもしれない。

2008年12月12日金曜日

newLISP v.10.0 インストールメモ

newLISP v.10.0 がリリースされていました

newLISP v.10.0 Release Notes

インストーラも用意されていますが、せっかくなので野良ビルドしてみようかなと 以下手順を列挙

Makefile の typo 修正:
41c41
<  @echo "  make linux           # newlisp for LINUX
---
>  @echo "  make linux           # newlisp for LINUX"
47,48c47,48
<  @echi "  make debian          # newlisp for UBUNTU debian with readline support"
<  @echi "  make debian_utf8     # newlisp for UBUNTU debian UTF-8 with readline support"
---
>  @echo "  make debian          # newlisp for UBUNTU debian with readline support"
>  @echo "  make debian_utf8     # newlisp for UBUNTU debian UTF-8 with readline support"

Win32の場合

MinGW-5.1.4 gcc を使う (cygwin gcc ではコンパイル出来なかった) makefile_mingw (or makefile_mingw_utf8) の書き換え (cygwinインストールしてなかったら /cygdrive ではないよなあ...)
CC=/cygdrive/c/MinGW/bin/gcc
STRIP=/cygdrive/c/MinGW/bin/strip

> make mingw_utf8

Linux (Vine 4.2)の場合

# apt-get install readline-devel  ; 必要ならば

$ make linux_utf8_readline
$ make test
$ make install_home

まあ特につまづくような所はないです

出来上がった newlisp バイナリのサイズは 200Kバイト前後とかなり小さい ソースファイルも少ないから、頑張れば読めるかもね

でもインデントがすげー独特なんですが…

2008年12月7日日曜日

あったら便利だと思う

パス名も setf で変更できたらいいのに

(setf (pathname-type "/home/kosh/foo.l") "lsp")
;=> "/home/kosh/foo.lsp"

パッと思いついたもの。 これだと置換する文字数同じでないとダメだし、ファイル名とディレクトリ名が被ると面倒だなあ。やはり make-pathname だろうか

(and (setf (subseq #1="C:/home/xyzzy/xyzzy.exe"
                   (search (pathname-type #1#) #1#))
           "mp3")
     #1#)
;=> "C:/home/xyzzy/xyzzy.mp3"

[追記] 2008-12-13T20:16:43+09:00

初めから make-pathname 使おうと思わないのはまだCL脳でない証拠か

(make-pathname :defaults "C:/home/xyzzy/xyzzy.exe"
               :type "mp3")
;=> #P"C:\\home\\xyzzy\\xyzzy.mp3"

パスネームの構造をよく知らないんだが、リストもしくは構造体で表現されているならば setf が使えてもいい気がするのになあ こんな感じに

((:device "C")
 (:host nil)
 (:directory ("home" "xyzzy"))
 (:name "xyzzy")
 (:type "exe")
 (:version nil))

ま、あくまでも仮定の話だしね

2008年12月6日土曜日

メモ化のメモ

再帰関数を効率の良いコードに直せ、と言われてパッと思い浮かぶのが

  • ループに還元
  • 末尾再帰
これくらいしかないと思ってた。メモ化なんて選択肢もあるのね。

計算コストの高い関数を同じ引数で複数回呼び出したいときがあるなら,値をメモワイズ(memoize)しておくのが得だ.以前の返り値をみな保管しておき,関数が呼び出される度にまず保管場所を見て,値が既に得られていないか調べる.

memoize 関数のコードはここでは省くとして、ハッシュをキャッシュに使えるのかと知ってひとつ賢くなった気分。

;; ベタベタな2重再帰フィボナッチ定義
(setf (symbol-function 'fibo)
      (memoize #'(lambda (n)
                   (cond ((= n 0) 0)
                         ((= n 1) 1)
                         (t (+ (fibo (- n 1))
                               (fibo (- n 2))))))))
(time (fibo 1000))     ; on xyzzy
Real time: 3.219 sec.
;=>4346655768693745643568852767504062580256466051737178040248172908
   9536555417949051890403879840079255169295922593080322634775209689
   6232398733224711616429964409065331879382989696499285160037044761
   37795166849228875

速っ!

ここでもうひとつ、最近自分が弄っている newLISP でもメモ化を使う方法があるのでご紹介を。 といっても newLISP にはハッシュ関数がないのでコンテキストで代用ですが。

Speed up with memoization -- newLISP Code Patterns
(define-macro (memoize mem-func func)
  (set (sym mem-func mem-func)
       (letex (f func  c mem-func)
         (lambda ()
           (or (context c (string (args)))
               (context c (string (args)) (apply f (args))))))))

(memoize fibo (lambda (n)
                (cond ((= n 0) 0)
                      ((= n 1) 1)
                      (true (+ (fibo (- n 1))
                               (fibo (- n 2)))))))
(time (fibo:fibo 100))
;=>0

newLISP のコンテキストは Common Lisp の簡易版パッケージ機能と考えていいでしょう。たぶん。 ここではキャッシュ用に名前空間 fibo を作成して、そこに関数値を放り込んだり参照したりしてます。

;; コンテキスト使用例
; 検索
(context MAIN "define")         ;=> define <407ee9>
(context MAIN "kosh")           ;=> nil

; 登録
(context MAIN "kosh" "lisper?")
(context MAIN "kosh")           ;=> "lisper?"
※ただし戻り値の正確さは (fibo:fibo 90) くらいで限界なんですが…多倍長整数に弱い?

2008年11月30日日曜日

xyzzy を再起動させる

新しいネタを提供する気力がないので手元にある既存のものを。

(defun reboot-xyzzy (&optional cleanup)
  "xyzzyを再起動する."
  (interactive "P")
  ;; ついでに初期化する
  (when cleanup
    (delete-file (si:dump-image-path) :if-does-not-exist :skip)
    (let ((siteinit (find-load-path "siteinit")))
      (and siteinit
           (string-match "\\.lc$" siteinit)
           (delete-file siteinit))))
  (cond ((modified-buffer-exist-p)
         (error "保存をしよう、な!"))
        ((running-process-exist-p)
         (error "サブプロセスが走ってるで")))
  (and (call-process (merge-pathnames "xyzzy.exe" (si:system-root))
                     ;; 環境変数の設定
                     :environ `(("XYZZYHOME" . ,(si:system-root))
                                ;; 設定ファイル (xyzzy.ini等) のディレクトリ
                                ("XYZZYCONFIGPATH" . ,(user-config-path)))
                     :no-std-handles t
                     :exec-directory (si:system-root)
                     :show t
                     :wait nil)
       (kill-xyzzy)))

(defun modified-buffer-exist-p ()
  "保存する必要のあるバッファがあるか."
  (remove-if-not #'need-buffer-save-p (buffer-list)))

(defun running-process-exist-p ()
  "動作中のプロセスがあるか."
  (some #'(lambda (pid)
            (eq (process-status pid) :run))
        (process-list)))

使い方:

  • 再起動させる M-x: reboot-xyzzy

  • 再起動のついでに、ダンプファイルと siteinit.lc を削除して再ダンプさせるようにする。 C-u M-x: reboot-xyzzy

注意:

保存していないバッファがあったりサブプロセスが走っていたりすると、「いいの?」って質問されて止まっちゃうのでそこはエラーにしてます。保存関係は (save-some-buffers) 使ってもいいかも。

あと、自分は xyzzy.ini ファイルを $XYZZY/usr/ ディレクトリ直下に置いてあるで、普通に使ってる人は ("XYZZYCONFIGPATH" . ,(user-config-path)) の部分を削除して下さい。

紙一重で動いてるスクリプトな気がするのでご使用は各自の責任でお願いします。

2008年11月28日金曜日

ネット断食

この間の日曜日未明からルータの調子がおかしかったのかネットワークから遮断された生活を送っていました。

今日の夕方からようやく回復。しかし原因は不明。

まあ、あれだ。
ネットがなくても生きていけることを身を以て知ることができただけ良しとしようか。

2008年11月22日土曜日

CL のライブラリを眺める [Metatilities]

今日行われる CL勉強会@Lingr の予習を少し。

生 REPL くらいしか触ったことがないから CL のライブラリを見るのは今回が初めて。 そんな CLer 初心者なのでパッと見た感じの感想だけ書いておきます。 勉強会はおとなしく ROM かな。

  • Metatilities はユーティリティライブラリ
  • CL のライブラリを読み込ませる方法が分からない
  • インストールは ASDF Linux のパッケージシステム(RPM とか YaST とか)、xyzzy の NetInstaller みたいなもの?
  • かろうじて分かったもの
    • anaphoric.lisp (OnLisp からアナフォリックマクロ)
    • graham.lisp (こちらも OnLisp から)
    • source-compare.lisp (diff?)
    • dates-and-times.lisp ($XYZZY/lisp/timestmp.l みたいなの)
  • #.(concatenate 'string *debug-doc-string*) debugging-utils.lisp より READ 時に解析して DOCSTRING を生成する方法
  • (defun (setf ~)) って何者?
  • C 言語で #ifdef を使い分けるのと同じくらい CL には #+#- が多い気がする

2008年11月20日木曜日

アロンアルファ棒

半年前に買ったのを使おうとした結果がこれだよ。

2008年11月18日火曜日

[xyzzy] newLISP 環境を構築してみたい

※一応 Emacs 版もあります。

newLISP に付属のエディタが使いにくい。

やっぱり使い慣れたエディタがいいよね、ということで xyzzy から newLISP シェル (REPL) を操作する簡単な xyzzylisp を書いてみた。

(defvar *newlisp-exe*
  (merge-pathnames "newlisp.exe" (si:getenv "NEWLISPDIR")))

(defun run-newlisp ()
  (interactive)
  (let ((*eshell* (format nil "~A -C -w ~A"
                          *newlisp-exe* (default-directory)))
        (*shell-mode-hook*
         (list #'(lambda ()
                   (rename-buffer "*newLISP*"))))
        ;; プロセス間通信は UTF-8
        (*default-process-encoding* *encoding-utf8n*)
        ;; なくても多分動く
        (*default-fileio-encoding* *encoding-utf8n*))
    (shell)))

;;; elisp ならこんな感じか
(defun run-newlisp ()
  (interactive)
  (let ((default-process-coding-system '(utf-8 . utf-8)))
    (run-lisp (format "C:/PROGRA~1/newlisp/newlisp.exe -C -w %s"
                      (expand-file-name default-directory)))))

ただし複数行の式を書くときは [cmd]~[/cmd] タグを使うことに注意。これは仕様だから仕方ない。
Evaluating newLISP expressions - newLISP v.9.4.5 Users Manual and Reference

書いていて気づいたが、 newLISP では日本語処理に難があるので UTF-8 が使えるバージョンを入手する必要がある。
http://www.newlisp.org/downloads/UTF-8_win32/

;; 比較
;; win32 で標準インストールされる newlisp.exe
> (explode "はろー")
("\227" "\129" "\175" "\227" "\130" "\141" "\227" "\131" "\188")
> (length "はろー")
9
;; UTF-8 の使える newlisp.exe
> (explode "はろー")
("は" "ろ" "ー")
> (length "はろー")
9
> (utf8len "はろー")            ; UTF-8 用 length
3

でも reverse 関数には日本語が使えないので泣く泣く次のように書く必要がある。 メンドクサ

> (apply append (reverse (explode "はろー")))
"ーろは"

さーて、何をしようか。

トラックバックはじめました

HaloScan のサービスを使わせてもらうことに。 HaloScan.com - Weblog Commenting, Ratings and Trackback

参考にした記事 GoogleBlog(Blogger)におけるTrackBackの方法

気をつけるべき点は、 Sineup した後で

Members->Settings
---
Time Zone を "(GMT + 9:00) Osaka, Sapporo, Seoul, Tokyo"
Character Encoding を "UTF-8"

の設定を忘れないことくらいか。まだ使ってないから知らないけど。

さっそく参考にさせてもらった記事の方にトラックバックpingを送りつけようと思ったらトラックバックURLが見つからない罠。


2008-11-18T22:27:12+09:00 [追記]

ブログのテンプレートいじくってたら元に戻らなくなったw なんでテンプレのアップロードがエラーになるんだろう…


2008-11-19T22:50:22+09:00 [追記]

アップロードエラーはログインし直したらいつの間にか直った。

で、HaloScan にはトラックバックの他に投票(はてなスターみたいなの)とかコメント管理の機能もあるらしいんだけど、コメントは Blogger のものを使うことに。

テンプレのいじり方は以下を参考に

Blogger beta に HaloScan をとりつける - クリボウの Blogger Tips

これでコメントリンクの横に "Trackback(#)" の文字が表示された。

NoScript(Firefoxアドオン)使っている場合は"haloscan.com"を許可しないと見えないかも。

まだ使ってないんで、トラックバック失敗するようならコメントの方でお願いします...

ないないない

newLISP なる LISP 処理系に手を出してみた。 付属のデモを眺めてみた感じ、LISP というよりもスクリプト言語という印象。

しかし困った点がいくつか...

  • Common Lisp との挙動の違い
    以前の記事で書いた通り。これは慣れるしかない、か。
  • 日本語のドキュメントが少ない
    検索で見つかる日本語の記事が少ないのです。 ただでさえ少ない(と思われる)LISP人口で、newLISP を使う日本人がどれだけいるのか。 自分が newLISP を知るきっかけになった方の記事はまだ続いてるみたい。参考にさせてもらってます。
  • サンプルコードが少ない
    日本語ドキュメント以上にこれは正直キツイ。 インストールディレクトリにもサンプルはいくつかあるのだが、どうしてだろう、コードがすごく見づらい。 全部タブインデントとか、束縛していない変数に代入とか。 開発者の中に Emacs 使いはいないのだろうか?
  • 付属の GUI 開発環境が使いにくい
    newLISP(win32) をインストールするとデスクトップとスタートメニューに newLISP-GS なるショートカットが出来上がる。 これを実行するとエディタ兼 newLISP 実行環境の GUI が立ち上がるのだが、Emacs キーバインドに飼い馴らされた自分には使いにくいことこの上ない。
  • 何ができるかよく分かっていない
    自分の問題です。すいません。

あと自分の記事をみて newLISP に興味を持ってくれた方がいて軽く吹いた。 http://cadr.g.hatena.ne.jp/mokehehe/20081111/newlisp

2008年11月14日金曜日

なにこれ・・・

ニコニコ動画にアップロードされている動画を音声ファイル(mp3)に変換するWebサービス「nicomimi」が、えらく混雑している。キャプチャは昨日の 22:30 くらいの。

同じサービスを運営しているこっちが現在メンテナンス中(08/11/13 2:00 ~ 08/11/14 17:00)ということもあるのだろうが、ユーザも少しは自重しろと言いたい。

2008年11月11日火曜日

newLISP と CL/Scheme の比較

意訳、省略を含むので参考程度に眺めることを推奨します。

ところで newLISP ってなに?


Comparison to Common Lisp and Scheme

このページでは newLISP と他の標準な Common Lisp, Scheme とのいくつかの違いを明らかにする。newLISP 特有のアプローチを知りたいなら About を読んで下さい。

  1. CL と異なり、newLISP と Scheme は式のオペレータ部分を(引数に適用する前に)評価する。
  2. ラムダ式は自身を評価する。それらはリストの副タイプであり、他のリスト同様に扱えるファーストクラス・データオブジェクトである。

    newLISP ではラムダ式の自由変数の束縛はラムダ式の適用時にのみ行われる。

    newLISP のラムダ式は関数定義の後いつでも処理できる。
    (last (lambda (x) (+ x x))) => (+ x x)
    See the page: Scheme のクロージャと newLISP のコンテキストの比較
  3. 変数、関数の名前空間を共有している (Lisp-1)。 シンボルは大文字小文字を区別する。
  4. 全ての変数は基本的に動的スコープ。 けれども自身のコンテキスト内で定義することにより、静的/レキシカルスコープが得られるし、変数捕捉を避けることができる。
  5. ドット対が存在しない。代わりにセルがリストの一部ならば、そのセルは一つのオブジェクトと他のオブジェクトへのポインタを含む。ゆえに cons の挙動が他の LISP 処理系と異なる。
    ;; Common Lisp and Scheme
    (cons 'a 'b) => (a . b)         ; ドット対
    
    ;; newLISP
    (cons 'a 'b) => (a b)
    
    ;; LISP cells in Common Lisp and Scheme
    (cons '+ (cons '2 (cons '3 (cons (cons '* (cons '4 (cons '3 nil))) nil))))
    => (+ 2 3 (* 4 3))
    
    ;; LISP cells in newLISP
    (cons '+ (cons 2 (cons 3 (cons (cons '* (cons 4 3))))))
    => (+ 2 3 (* 4 3))
  6. ユーザ定義の関数の引数は全てオプショナルである。 代入されなかった引数は関数内部では nil と仮定される。
    ((lambda (x y z) (list x y z)) 'foo 'bar) => (foo bar nil)
  7. 必然的に、未束縛の変数は存在しない。 そのようなシンボルは最初の構文解析時に生成されて、現在の名前空間に nil として束縛される。
    (list foo bar baz) => (nil nil nil)
  8. ブール定数は nil/true 。 CL では nil はリストの終端という役割もある:
    ;; Common Lisp
    (cons 'x nil) => (x)
    
    ;; newLISP
    (cons 'x nil) => (x nil)>
    Scheme のブール定数は #t/#f が真/偽。
  9. シンボルとコンテキストを除く全てのオブジェクトは一度だけ参照される。 (ORO: Only Referenced Once) このルールにより CL, Scheme で使われる伝統的なガベージコレクションアルゴリズムなしに、自動的で、スタックベースなメモリ管理が出来る。
  10. マクロは引数を評価しない関数のようなもの。 結果としてマクロは組み込まれた基本関数のように振舞う。
    (define-macro (my-setq x y) (set x (eval y)))
    ;; もしくは変数捕捉を避ける衛生的なマクロとして
    (define-macro (my-setq) (setq (args 0) (eval (args 1))))
    変数を明示的に展開する組み込み関数として expand, letex がある:
    (let ((z 2))
      (expand (fn (x y) (* x y z))   ; fn は lambda の略記
              'z))
    => (lambda (x y) (* x y 2))
    
    (define (raise-to power)
      (expand (fn (base) (pow base power)) 'power))
    
    (define square (raise-to 2))
    (define cube (raise-to 3))
    
    (square 5) => 25
    (cube 5)   => 125
  11. 等価条件の判定は = のみ。 newLISP では値をコピーして渡すスタイルをとっている。
  12. インデックス機能 (implicit indexing)
    (let ((myList '(a b c (d e) f g))
          (v '(3 1)))
      (list (nth 2 myList)          ; using nth
            (myList 2)              ; using implicit indexing (1)
            (myList -3 0)           ; using implicit indexing (2)
            (myList v)              ; with address vector
            (1 2 myList)            ; implicit rest, slice
            ))
    ;=>(c c d e (b c))

newLISP の内部についての詳細は以下を参照のこと:


Change Log

  • 2008-11-11 訳してみたので公開
  • 2008-11-30 加筆修正
    高階関数 (higher-order function) とファーストクラスオブジェクト (first-class object) はどう見ても別物です。なに勘違いしてるんだ自分…

2008年11月3日月曜日

CLISP on Cygwin が使えない

半年くらい前からだろうか、cygwin にインストールした clisp が起動と同時にエラーになる症状に陥った。

$ clisp
  i i i i i i i       ooooo    o        ooooooo   ooooo   ooooo
  I I I I I I I      8     8   8           8     8     o  8    8

*** - Internal error: statement in file
       "/usr/src/clisp/clisp-2.46-1/src/clisp-2.46/src/stream.d"
      , line 5658 has been reached!!
      Please see <http://clisp.cons.org/impnotes/faq.html#faq-bugs> for bug
      reporting instructions.
Break 1 [3]> nil
NIL
Break 1 [3]> (car '(1 2 3))
1
Break 1 [3]> :q

[/usr/src/clisp/clisp-2.46-1/src/clisp-2.46/src/eval.d:573] reset() found no driver frame (sp=0xdacc70-0xd90580)
Floating point exception (core dumped)

動かないわけではないけれど、デバッガから抜けると落ちる。

まあ win32 バイナリの clisp の方が使えるから問題はないが。けど気持ち悪いなあ。 解決法がわからないから今のところ放置中。

2008年11月2日日曜日

[xyzzy] EDICT とべんりすぷ

なんとなく xyzzy の辞書ファイルを更新しようとしたらちょっと驚いた。 なんでかというと、その辞書ファイルの元となる EDICT ファイルが現在も更新されていたから。

xyzzy で辞書を使う方法は XyzzyWiki か $XYZZY/etc/README.gendic を参照のこと。 2008-11-02 現在、 Wiki の edict へのリンクは切れてます。 編集したいけどページが凍結されている…

辞書ファイル配布元: The Monash Nihongo ftp Archive http://ftp.monash.edu.au/pub/nihongo/00INDEX.html トップページから "edict.gz" を検索すれば見つかるはず。大分上の辺り。

で、少し古くても構わないのならば同じ辞書ファイルは sdic (Emacs 用辞書引きメジャーモード) の作者さんである土屋さんのページからも入手できる。

[利用・配布条件] を見ると 1998 年のものらしい。 http://www.namazu.org/~tsuchiya/sdic/

二つの辞書ファイルを比較すると (比べたのは edict ファイル本体のみ)

更新時間        サイズ          行数
---             ---             ---
2008-10-31      9.1M            160,090行
1998            2.5M             64,152行

すごく大きいです。 当然ながら出来上がる辞書ファイル xyzzy{dic,e2j,idi,jrd} も大きくなった。

しかし更新したのは良いけれど、その違いがわかるほど辞書を使いこなしていない自分にがっかりした。

辞書ファイル作成、辞書引きまで全部 LISP で作れないかなあ。


[おまけ] 自分の使っている便利 LISP 紹介


;;; バッファの代わりにポップアップ表示で辞書引き
(defun lookup-current-word ()
  (interactive)
  (let ((msg (save-window-excursion
               (lookup-e2j-dictionary-selection)
               (prog2
                   (set-buffer "*dictionary*")
                   (buffer-substring (point-min) (point-max))
                 (delete-buffer "*dictionary*")))))
    (popup-string (string-trim (string #\LFD) msg) (point))))

(global-set-key '(#\C-c #\e) 'lookup-current-word)
こんな感じになる。

2008年10月24日金曜日

整数を3けた区切り形式の文字列に変換する関数

この間の日曜日、基本情報処理技術者試験を受けに行ってきました。 合格するかどうかは正直微妙…

そこでこんな設問が

午後の問題 問6: [プログラムの説明] 金額を表すときのように、整数を3けた区切り形式の文字列に変換する関数 convert である。(以下略)
           表 変換例
┌────┬────────────┐
│  整数  │3けた区切り形式の文字列│
├────┼────────────┤
│1234567 │1,234,567               │
│ -57482 │-57,482                 │
│     63 │63                      │
│-999999 │-999,999                │
└────┴────────────┘

C言語での問題だったけど、なんとなく Lisp で解いてみた。
# 別に解答間違ったからその腹いせにじゃあないです :-(


((lambda (int &optional (interval 3) (comma ","))
   (reverse
    (with-output-to-string (*standard-output*)
      (with-input-from-string 
          (in (reverse (princ-to-string (abs int))))
        (do ((i 1 (1+ i)))
            ((not (listen in)))
          (princ (read-char in))
          (if (and (zerop (mod i interval))
                   (listen in))
              (princ comma)))
        (if (minusp int)
            (princ "-"))))))
 -123456789)                    ; => "-123,456,789"

でも CLer なら一行で十分だよね、というオチ。

(format nil "~:D" -123456789)   ; =>"-123,456,789"

2008年10月23日木曜日

elispでarglist関数っぽいの

「xyzzyでarglist関数っぽいの 」[その1][その2] のおまけ。

何でこんなの作ったんだろう…

;;; for Emacs Lisp
(defun arglist (def)
  (unless (keymapp def)
    (let ((argstr (car (help-split-fundoc (documentation def) def))))
      (if argstr
          (format "%s" (cdr (car (read-from-string argstr))))
        (help-function-arglist def)))))

(arglist 'arglist)        ; (def)
(arglist 'lambda)         ; "(ARGS [DOCSTRING] [INTERACTIVE] BODY)"
(arglist 'if)             ; "(COND THEN ELSE...)"
(arglist 'ctl-x-4-prefix) ; nil

参考にした関数: describe-function-1 (lisp/help-fns.el)

elisp では関数定義の引数とドキュメントに書かれている引数が異なる場合があるので、そのときは後者を優先します。ややこしいなあ。

(help-function-arglist 'lambda)
=> (&rest cdr)
(car (help-split-fundoc (documentation 'lambda) 'lambda))
=> "(lambda ARGS [DOCSTRING] [INTERACTIVE] BODY)"

2008-10-25T00:40:33+09:00 [追記]

read-from-whole-string 関数 (lisp/thingatpt.el ファイルで定義されている) は標準で使えるものでないので read-from-string に変更。

とりあえず「特殊」だということは分かった

CLのスペシャルに秘められた罠(?)」より

関係あるっぽい記述を見つけたのでメモ。

SPECIAL-OPERATOR-P - CLHS

Notes:

Historically, this function was called special-form-p. The name was finally declared a misnomer and changed, since it returned true for special operators, not special forms.

注意: (適当訳) 古くから、この関数は special-form-p と呼ばれていた。 この関数はスペシャル・フォーム(リスト)でなく、スペシャル・オペレータ(シンボル)が与えられる場合に真を返すことから、この名前は最終的に誤った名前であると認められ改定された。

仕様を決めていた中の人も勘違いしていたようで。

しかしなぜ勘違いしたのか、なぜ長い間改定がなかったのかは、気になる。 CLtL2 には special-form-p しかないなあ。ANSI-CL で変更されたのかな。

ちなみに xyzzy でも special-form-p しかないのでサクッと定義。 もちろん LISP パッケージの中ですよ、奥さん。

(in-package :lisp)

(export '(special-operator-p))

(defun special-operator-p (symbol)
  "Returns true if symbol is a special operator,
otherwise, returns false."
  (special-form-p symbol))

2008年10月21日火曜日

読書感想文

Shibuya.lisp テクニカルトーク #1 (Ustream.TV)での配信をリアルタイムで見てました。

# 途中あまりの眠気に落ちたのはナイショ

Lispを使った実例を見れた(聞けた)ところ、これが良かった。 Lispで作られたアプリの例をあまり知らないので。

一番印象に残ったのはmitamex4uさんの「俺Lisp」の話。 ケータイで動くREPLを見てみたいです。

もう少し文章書いていた気がするんだけど、気に入らない部分を添削し てたらこんなに短くなってしまったよ。

2008年10月19日日曜日

なんでだろう

「APIを叩く」って言葉は誰が言い出したんだろう。

「叩け(リクエストすれ)ば埃(レスポンス)が出る」みたいな意味合いだと思うが。 うまい表現だなあと思った。それだけ。


2008-11-01T03:46:40+09:00 [追記]

コンピュータ用語として「叩く」を取り上げていたページ

叩く - 通信用語の基礎知識
プログラミングに於いては、ハードウェアの接続されているI/Oポートにアクセスすることを "ポートを叩く" と表現する。
日本版 Jargon File
vt. プログラマが「叩く」ものはポートである。英語ではもっぱら `bashing' が使われるが、それと対応している。

まあ、スラングなんでしょう。

2008年10月15日水曜日

[xyzzy]外部コマンドの結果を文字列として出力する

command-substitutionという関数があります。けっこう便利。

上の関数を、バッファを介せずにCLの標準関数だけで作れないかなーと 思ってこんなものを書いてみた。まだまだ処理系依存。

やっていることは以下の3点

  1. 外部コマンドを呼び出して、結果をファイルに保存
  2. 保存したファイルの中身を文字列ストリームに流す
  3. ついでに右端の空白文字を取り除く
(defun cat (file &optional stream)
  "print file contents."
  (with-open-file (fp file)
    (do ((ch (read-char fp nil nil)
             (read-char fp nil nil)))
        ((null ch))
      (princ ch stream))))

(defun shell-command-to-string (command)
  "Execute shell command COMMAND and return its output as a string."
  (let ((outfile (make-temp-file-name "xyzzycmd-")))
    (unwind-protect
        (with-output-to-string (stream)
          (call-process command :output outfile
                        :show :minimize :wait t)
          (cat outfile stream))
      (delete-file outfile))))

(defun command-substitution (command)
  (string-right-trim '(#\SPC #\TAB #\LFD)
    (shell-command-to-string command)))
結果:
(command-substitution
 (format nil "ls -1 ~A"
        (merge-pathnames "*.exe" (user-homedir-pathname))))
=>
"C:/home/xyzzy/xyzzy.exe
C:/home/xyzzy/xyzzycli.exe
C:/home/xyzzy/xyzzyenv.exe"

(command-substitution "echo 舌足らずなブログ") => "舌足らずなブログ"

意図してなかったが日本語も通るようだ。

ちなみにshell-command-to-stringという関数名はEmacsから

後は

  • 一時ファイルを作成する関数 (make-temp-file-name)
  • 外部プロセスを処理する関数 (call-process)
がCLにあれば尚良いんだけど…

2008年10月14日火曜日

第20回CL勉強会@Lingrセルフ反省会

勉強会ログ g000001さんによる勉強会まとめ

前半: Let Over Lambda 概略 2~3章まで (g000001さん)

筆者が別だから当然だけど、OnLispとは違った側面でマクロを使ってい る印象。マクロの導入部分はどうしても他のマクロ本と似たり寄ったり の説明になってしまうのかも。ちょっと勘違い。

xyzzyはインタプリタとしてしか使ってないから、コンパイルする際の マクロ展開やら変数補足やらを気にしたことなんてなかったなあ。

後半: CL永続化入門 (onjoさん)

GoogleDocsによるスライドはとても見やすかったです。

xyzzyが吐き出すダンプファイルもLispのデータを保存している例では なかろうかと思ったり思わなかったり。

(si:dump-image-path)  ; => "C:/home/xyzzy/xyzzy.wxp"

次回の勉強会は10/25です。 (10/18はShibuya.lispのためお休み)

2008年10月12日日曜日

チャットにおける「>」「<」の使い方

チャット上での用語・表現 - Wikipedia
「>」「<」 特定の相手にコメントする場合に用いる簡易接続詞。「>」は左にコメントを、右に固有名詞を入れて二人称的に扱う。例えば「こんばんは>スズキ」なら「スズキさん、こんばんは」という程度の意味だが、「落ちます>ALL」なら、参加者全員にお別れを云っている事になる。「<」は三人称的に用いるが「大変ですね<イトウ」なら、「イトウさんは大変なんですね」といった感じで問い掛けているような意味に使われる。これらの場合、固有名詞は大抵、敬称を略して使われる。

Lingrのログを参考にして適当に使っていたけれど、それなりの意味があったと は知らなんだ。「>」ばかり使っていた気がする。

Wikipediaだと「<」の説明がよく分からなかったので他にも検索してみた

多分こんな感じ

Ω 自分の発言 < 話題 > 相手(HN)

シェルのリダイレクト記号みたいな使い方と考えてもOK?

$ command < input > output

誰か教えてくだしあ

しかし敬称略はちょっとねぇ…

2008年10月10日金曜日

.emacs >>> .xyzzy (いろんな意味で)

たまにVineLinuxを使っています。

これに付属のEmacsの標準背景色(紫)がキーワードの色と軽くかぶっていて見づ らいのです。仕方ないから配色変えようかとelispを弄りだしたのが失敗だった。

「そういえばこっちの設定ファイル、Windowsのより古かったな」

「namestringって使えなかったっけ?」

「関数調べるの面倒くさいな(info-modokiとHyperSpecに慣れていたので)」

とかなんとか愚痴りながら作業してたら、いつの間にか日付が変わっていた。

ナンテコッタイ

とりあえず

elispにもCommonLispのような仕様書があれば良いのに、と思った。 関数定義に一発でジャンプできるのがいいなあ。関数のdoc-stringとか、 elisp-infoもあるけど、慣れていないと使いづらい。

あと、パッケージの概念がないから標準関数と拡張関数が同じように見えて しまう点が気に入らない。 (apropos '("buffer")) とか正直萎える。

CLとelispの両刀遣いになるのは難しい…

(結局まだ配色変えてない)

2008年10月6日月曜日

第19回CL勉強会@Lingrセルフ反省会

お題は Let Over Lambda 1. Introduction から 2.3. Lexical and Dynamic Scope くらいまで

# g000001さん、いつも進行役お疲れ様です。勉強させてもらってます。

勉強会ログ

以下、気になったことの列挙と感想

エクステント

スコープは、「どこ」で参照できるか(空間的な意味)
エクステントは、「いつ」参照できるか(時間的な意味)

静的バインディングは不定エクステントをもつ
動的バインディングは動的エクステントをもつ

で、参照がなくなった実体は破棄される(GC)

これが噛み砕ければ、また一歩CLに関して賢くなれそうだ Common Lisp の スコープ と エクステント (PDF)

マクロはLISPの構文木に対して作用する (Macros change the syntax of lisp code.)

これかな?

ハッカーと画家」p181より

LispのプログラムコードはLispのデータオブジェクトからできている。
それは、ソースコードというようなつまらない意味じゃない。Lispのコ
ードは、ひとたびパーザによって読まれたら、あなたが解析することが
できるデータ構造になるんだ。
コンパイラの動作を理解していれば、Lispの構文は奇妙だと言って
Lispには構文がないといっても大した違いはないということが分かるだ
ろう。他の言語ならコンパイラが構文解析して内部に作られる構文木を、
Lispでは直接プログラムとして書き下すわけだ。しかも、この構文木は
プログラムからアクセスできるから、構文木自身を操作するプログラム
を書くことができる。Lispではそのようなプログラムをマクロと呼ぶ。
いわば、プログラムを生成するプログラムだ。

マクロなしLISPをBulbと呼ぶ (A language without lisp macros is a Bulb.)

良くデザインされたユーティリティは、それらを組み合わせて使う ことにより個々の生産性の総和を超えるものを得ることができる (well-designed utilities can, when combined, work together to give a greater than the sum of the parts productivity advantage.)

UN*Xのコマンド群とシェルスクリプトはこれの良い例だろうね

Lispにおける大域変数とスペシャル変数の違いがいまいち分からない

2008年10月5日日曜日

[xyzzy]arglist関数を作り直した

以前書いたarglist関数の見た目がやっぱりダメダメなことに気づいた。

(filename &key (direction :input) (if-does-not-exist "FIXME") ...)
=> (filename &key (direction input) (if-does-not-exist FIXME) ...)

ということでまたarglistを書き直した。 キーワード以外のシンボルにフィルターをかけるような関数をつくって解決。

# 何かまだバグがありそうで恐い…

(defun arglist (x)
  (labels ((arglist-helper (x)
             (cond ((consp x)
                    (mapcar #'arglist-helper x))
                   ((and (symbolp x) (not (keywordp x)))
                    (intern (string x)))
                   (t
                    x))))
    (arglist-helper (arglist-1 x))))

letがなければlambdaを使えばいいじゃない

「letはlambdaのシンタックスシュガー」という話を聞いたので。

letのマクロによる実装 (OnLispのコードより)

(defmacro our-let (binds &body body)
  `((lambda ,(mapcar #'(lambda (x)
                         (if (consp x) (first x) x))
                     binds)
      ,@body)
    ,@(mapcar #'(lambda (x)
                  (if (consp x) (second x) nil))
              binds)))

(macroexpand '(our-let ((x 1) (y 2)) (+ x y)))
=> ((lambda (x y) (+ x y)) 1 2)

スペシャル変数も束縛可能

(list (our-let ((*features* '(:hoge1 :hoge2 :hoge3)))
        (member :hoge2 *features*))
      (member :hoge2 *features*))
=> ((:hoge2 :hoge3) nil)

2008年10月3日金曜日

関数の引数が微妙に違っているように見える

たとえばapply関数

CLtL2 の場合

apply function arg &rest more-args

HyperSpec の場合

apply function &rest args+ (arg がなくなっている)

HyperSpecの方の args+ はたぶん正規表現 「1個以上の~」 の意味で書かれ ているんだと思う。
(apply #'list-all-packages)とか無理だし。

結局、違っているように見えるけれど取り得る引数は同じみたい。 個人的にはCLtL2の書き方の方が見やすいなあ。

他にも map系関数, list* あたりが違っていた。まだあるかもしれない。

2008年10月2日木曜日

レッサーパンダ推奨

このブログ、ブラウザInternetExplorerから見るとレイアウトが若干崩 れるみたいです。特にLispのコード。

あと、複数行をコピーしたはずなのに貼り付けたら一行になってたりと か。これは正直なける…

ということでIEで見ている人がいたらブラウザをFirefoxに乗り換える ことを推奨します。GoogleChromeでもいいけどシンプルすぎる見た目が あまり好きになれないからFirefox。他のOSでも使えるしね。

Mozilla Firefox ブラウザ無料ダウンロード

2008年10月1日水曜日

[xyzzy]arglist関数おまけ

もう少し返り値を見やすくする方法に気づいたのでメモ。

こっちのarglistをarglist-1に置き換えて次の関数とマクロを追加

(defun arglist (x)
  (let ((args (arglist-1 x)))
    (if (consp args)
        (nth-value 0 (read-from-string (princ-to-string args)))
      args)))

(defmacro nth-value (n form)
  `(nth ,n (multiple-value-list ,form)))

(arglist-1 'apply)  ; (function lisp::arg &rest lisp::more-args)
(arglist 'apply)    ; (function arg &rest more-args)
(arglist 'if)       ; "TEST THEN [ELSE]"

2008-10-02T19:34:37+09:00 [追記]

ツッコミを入れられたらできるだけ返したくなる性分です。 困ったものです。

ついでなのでpaste.lisp.orgに貼ってみた。変更点は(nth-value ~)あたり。
http://paste.lisp.org/display/67774

これでキーワードシンボルはなるべくそのままにした関数の引数リスト を返してくれる(はず)です。keywordpなんて関数はじめて見た。

ただし、関数引数にキーワードシンボルが入るのは組み込み関数だけのようです。なのでLispで書いたLisp関数の引数はコロンなしのシンボルになります。 この挙動はCLISP,Allegroでも同じみたいなのでこのままで良いかな。

(arglist 'make-list)
(size &key :initial-element)    ; xyzzy
(#:ARG0 &KEY :INITIAL-ELEMENT)  ; clisp
(EXCL::SIZE &KEY EXCL::INITIAL-ELEMENT) ; acl
(arglist (lambda (fn seq &key key1 key2 key3)
           (list key1 key2 key3)))
(fn seq &key key1 key2 key3)    ; xyzzy
(FN SEQ &KEY KEY1 KEY2 KEY3)    ; clisp
(FN SEQ &KEY KEY1 KEY2 KEY3)    ; acl

# xyzzyはmodern(大文字と小文字を区別する)だから :test -> :TEST
# に変換する必要はないのかなあ

2008年9月30日火曜日

ほぼ日刊xyzzy新聞

タイトルはホッテントリメーカーから。日刊とか無理。

非同期って恐いねというお話。

まずは関数を3つ用意

(defun process-list ()
  "バッファプロセスのリスト."
  (remove-if-not #'processp
                (mapcar #'buffer-process (buffer-list))))

(defun running-process-exist-p ()
  "動作中のプロセスがあるか."
  (some #'(lambda (pid)
            (case (process-status pid)
              (:run t)
              (:exit nil)))
        (process-list)))

(defun kill-all-subprocess ()
  "バッファプロセスをすべて殺す."
  (every #'kill-process (process-list)))

次に何かxyzzyからプロセスを走らせる(M-x: shell とか)

で、ここで上の関数を使って動作中のプロセスをすべて消したいわけです。 別にタイミングを気にしなければ (kill-all-subprocess) だけでも良いんだ ろうけど、ちょっと時間差が気になるコードを書いていたのでこんなS式を書 いてみた。

これならloopフォームを抜けたときには動作しているプロセスが完全になくな っているはず。

(loop (if (running-process-exist-p)
          (kill-all-subprocess)
        (return t)))

意気揚々と実行(C-x C-e)。確かにプロセスは消えた。そこまではいいけど...

何故かxyzzy本体まで落ちた。

...

あるえー(・3・)

とりあえず次ので解決したことにしておく


(loop (if (running-process-exist-p)
       (and (kill-all-subprocess)
            (sleep-for 0.5))
     (return t)))

第18回CL勉強会@Lingrセルフ反省会

仮眠のつもりがぐっすりとか、どんだけだよ。

ということで過去ログを見ながらの勉強会参加でした。
お題はakaさんによる、CLのパーシステンス(persistence)のお話。

こういう概念、それをソフト・ハードウェアで実現する方法があるということを知 ることができたことが自分にとっての収穫でしょうか。リアルタイムじゃなかった のは残念だけど、勉強になりました。OSのハイバネーションとか、へー。 途中出てきた単語の"OO"って"Object-Oriented"(オブジェクト指向)の略かな?

あと、Schemeの継続はパーシステンスの一種なのかなと思った。 OnLispでの解説くらいでしか継続の知識無いからよく分からないけど。

後半のCLOS、AllegroCacheのあたりは正直さっぱりです。勉強します。

30分枠で勉強会の進行役か…話せるネタが無い…

2008年9月22日月曜日

xyzzyでarglist関数っぽいの


(defun arglist (symbol)
  "シンボル関数の引数をリストで返す."
  (if (symbolp symbol)
      (let* ((fn (symbol-function symbol))
             (cl (and (si:*closurep fn) (si:closure-body fn))))
        (cond ((si:*builtin-function-p fn)
               (get symbol 'si::builtin-function-argument))
              ((eq (car cl) 'lambda)
               (cadr cl))
              ((eq (car fn) 'macro)
               (cadr fn))))
    (error "not symbol: ~S" symbol)))

(arglist 'nth)            ; =>(lisp::n list)
マクロ使えば関数引数(arglist #'car)とかにも使えそうだけど、組み込み関数からのシンボルの取り出し方が分からない。

2008-09-25T19:36:19+09:00 [追記]

マクロで無理やり書き直してみた。見た目ほとんど変わってない。

(defmacro arglist (x)
  (let* ((fn (coerce (if (eq (car x) 'lambda)
                         x (cadr x))
                     'function))
         (cl (and (si:*closurep fn) (si:closure-body fn))))
    (cond ((si:*builtin-function-p fn)
           `(get (cadr ',x) 'si::builtin-function-argument))
          ((eq (car cl) 'lambda)
           `(cadr ',cl))
          ((eq (car fn) 'macro)
           `(cadr ',fn)))))
これで関数シンボルとかラムダ式とかにも使えるはず。

# マクロの側面しか見てない使い方だなあ…

2008-10-01T08:28:28+09:00 [追記の追記]

NANRI さんのコメントを受けてもう一度関数で書き直す。

横幅が狭いので多少インデントに無理があるのはご愛嬌ということで。

(defun arglist (x)
  (let* ((fn (or (and (consp x) (eq (car x) 'macro) x)
                 (coerce x 'function)))
         (cl (and (si:*closurep fn) (si:closure-body fn))))
    (cond ((si:*builtin-function-p fn)
           (labels ((symbol-builtin-function-name-p (sym)
                      (and (fboundp sym)
                           (si:*builtin-function-p 
                            (symbol-function sym))
                           sym))
                    (builtin-function-name (f)
                      (when (string-match "#<function: \\(.+\\)>"
                                          (princ-to-string f))
                        (match-string 1))))
             (get (some #'symbol-builtin-function-name-p
                        (find-all-symbols
                         (builtin-function-name fn)))
                  'si::builtin-function-argument)))
          ((eq (car cl) 'lambda)
           (cadr cl))
          ((eq (car fn) 'macro)
           (cadr fn)))))
;;; テスト
;;; 関数オブジェクト (組み込み、普通の、ラムダ)
(arglist #'car)                 ; (list)
(arglist #'arglist)             ; (x)
(arglist #'(lambda (n) (1+ n))) ; (n)
;;; シンボル (組み込み、普通の、マクロの)
(arglist 'car)                  ; (list)
(arglist 'arglist)              ; (x)
(arglist 'when)                 ; (lisp::test &body lisp::body)
;;; コンスセル (ラムダ式、マクロ)
(arglist '(lambda (n) (1+ n)))  ; (n)
(arglist #'when)                ; (lisp::test &body lisp::body)

いい感じです。

問題は実用性が無いことかな。

第17回CL勉強会@Lingrセルフ反省会

CommonLisp開発環境の話でした。

9/20第17回慢性的CL勉強会@Lingr 8時だョ!全員集合まとめ

以下、自分的に気になった点。

  • Linux使い多いなあ。Windowsじゃあまともに開発しにくいのはわかるが。
  • 1.括弧は最初に対で入力する派?2.先から書いて行く派?
    結構2が多くてびっくり。自分1です。 ()→(defun hoge)→(defun hoge (lst))という順番 手動で括弧を入れるから、たまにShiftキー離すタイミングがずれて"(9"とか入力してしまう罠。 ↓先から書いて行く派の方の意見
    # 最後のカッコをどりゃーっと入れていると爽快感がある。
    # なんていうか綴じ括弧を連続で入れてるとテンションが上がってくるんですよww
    なるほど。。。
  • onjoさんによるCommonLisp環境紹介@GoogleDocs
    コレ見てSLIMEに少し興味を持った。今使っているxyzzy環境の便利なところといえば
    • 関数、変数リファレンス (ldoc,funsig,info-modoki,hyperspec)
    • シンボル補完 (lisp-complete-symbol)
    の部分が大きいから(あと起動の早さ)、SLIMEでもこれらが使えるならEmacs環境もありかもしれないと思った。
  • CLISPの引数表示
    引数が見えにくいのは最適化されているから、らしい。 これはCLISPをソースからビルドする必要がありそう。
    (arglist 'nth)
    =>(#:ARG0 #:ARG1)
    ; マクロだと見える?
    (arglist 'defun)
    =>(SYSTEM::FUNCTION-NAME SYSTEM::LAMBDA-LIST &BODY SYSTEM::FORMS)
    # Ω kosh
    # CLISPごった煮のおかげでarglistが便利になりそうな予感w
    # Ω g000001
    # arglist機能は、30年前のLispマシンからある機能で、
    # これがない開発環境とか、むしろ30年前以前w
    ごめんなさい、ただの勘違いでした。 でもGCL(ANSI,CLtL1)にはarglist関数がなぜか無い
    [GCL]>(fboundp 'arglist) => NIL
    別の関数があるのかな…?
  • sbclも便利そう

2008年9月19日金曜日

文字列を分割する

NANRIさんとこのコードを参考にsplit-stringを繰り返しでやってみた。

# position関数内の:test関数の挙動がよく分かってませんが


(defun split-string (string &optional (separator #\space)
                            omit-nulls)
  (labels ((pos (str sep)
             (position sep str
                       :test #'(lambda (x y)
                                 (find y x :test #'char=)))))
    (let ((sep (string separator))
          (acc '()))
      (do* ((str string (subseq str (1+ n)))
            (n (pos str sep) (pos str sep)))
           ((null n)
            (nreverse (if omit-nulls
                          `(,str ,@acc)
                        (remove "" `(,str ,@acc)
                                :test #'string=))))
        (push (subseq str 0 n) acc)))))

(split-string "C:/home/xyzzy/xyzzy.exe" #\/)
=>("C:" "home" "xyzzy" "xyzzy.exe")
(split-string "system::typespec-alist" #\:)
=>("system" "typespec-alist")
(split-string "system::typespec-alist" ":" t)
=>("system" "" "typespec-alist")

グチャグチャになってしまった。
xyzzy/Emacsにはsplit-string関数があるけど、CLにはないから結構便利そう。

関数書いてから気づいたんだが、split-stringって正規表現使えるのね

(split-string "Soup is good food" "o*")
=>("S" "up is g" "d f" "d")

視点の問題、切り返しの問題

ちょっと感心した話。

その発想はなかったわ。 おばさん視点を書いたのはたぶん本人じゃないと思うけど、自分の言いたいことが相手に伝わらないことってよくあるよね、って思った。

いやそこまで深い話じゃないんだけどね。

自分の場合、伝えたいことが頭の中でとろけてしまって形にならないことがしばしばあって困る…

2008年9月17日水曜日

文字列に含まれる単語の最初の文字を大文字にする (LL Golf Hole 2)

どう書く?orgより LL Golf Hole 2 - 文字列に含まれる単語の最初の文字を大文字にする

kozimaさんの書いたコードを改造してみた。たぶん処理系依存でなくなったと思う。

(defun cap (str)
  (labels ((fn (x y)
             (if (and (upper-case-p x)
                      (lower-case-p y))
                 x
               y)))
    (map 'string #'fn
         str
         (string-capitalize str))))
(cap "LL day and night") ; =>"LL Day And Night"

やっていることといえば、

①元の文字列と②string-capitalizeした文字列の各文字をそれぞれ比べて、①の大文字部分が②で小文字に変わっていたら①の文字を、そうでなければ②の文字を選択しているだけ。

下のを縦読みするとなんとなく分かるかもしれない。

"LL day and night"        ; ①str
"Ll Day And Night"        ; ②(string-capitalize str)
------------------
"LL Day And Night"        ; 結果

そもそもstring-capitalizeの実装ってどうやるんだろう?

2008年9月15日月曜日

CommonLispでOpenID

取りあえずネタ投下。

OpenID Libraries for Common Lisp (CL-OpenID)

ちなみにBloggerではOpenIDが使えるとのこと。

初投稿

冷やし中華よろしくブログ始めました。

xyzzyを使い始めて、Lispに関する疑問やら何やらが溜まってきたのでこのブログに投げてメモしておこうかなという感じです。

"xyzzy"って何?という人は下のリンクをたどればいいと思うよ。