ラベル Lisp の投稿を表示しています。 すべての投稿を表示
ラベル Lisp の投稿を表示しています。 すべての投稿を表示

2009年5月26日火曜日

「10分でコーディング x 2 〜リストの破壊的操作篇〜」を解いてみる

乗り遅れた感はありますが、g00000さん出題のLISP問題を解いてみました。

10分でコーディング x 2 〜リストの破壊的操作篇〜 - わだばLisperになる - cadrグループ

リストの破壊的操作 - 日々ごちゃごちゃと考える 経由

(1) alistを破壊的にplistに変換する関数(nalist-to-plist)を書いて下さい。
(2) nplist-to-alistも作ってみて下さい。

まず自分がやったのは「リストの図を書く」こと。実際は紙の上で考えた。

;; 連想リスト:alist  '((foo . 1) (bar . 2))

   +-+    +-+
-->|1|--->|3|--->nil
   +-+    +-+
    |      |
    V      V
   +-+    +-+
   |2|->1 |4|->2
   +-+    +-+
    |      |
    V      V
   foo    bar

;; プロパティリスト:plist  '(foo 1 bar 2)

   +-+   +-+   +-+   +-+
-->|1|-->|2|-->|3|-->|4|-->nil
   +-+   +-+   +-+   +-+
    |     |     |     |
    V     V     V     V
   foo    1    bar    2

なるほど、確かにコンスセル数が同じだ。

で、このコンスセルを増やすことなくリスト操作を行うんだから →各コンスセルのcar/cdrの向きを操作すれば良さそうだ →じゃあrplaca/rplacdを使おう →出来上がり

...と書けば短いのに、それに気づくのに結構時間がかかった上、解答後に他の 人のコードを見てようやくrotatef/setfの存在に気づいたのはナイショです。

所要時間:(1)1時間(2)50分

しかも効率優先のための破壊的操作なのに無駄に再帰を使ったのもマイナスポイント。

CLの無い端末で作業をしていたのでEmacsLispでコーディング。 自宅のubuntu+SBCL 1.0.18+SLIMEでも動作確認しました。今回xyzzy使わなかったな...

2009年5月21日木曜日

「なぜJAVAなのか」と聞かれたら

細かい経緯は面倒なんで省略しますが、自分が大学内でLISPの話題をした際に 「なんでJAVAでLISPを動かす必要があるの?」 と質問されました。

特にこれはLISPに限った話ではなく、「なぜ他の言語をJAVAで実装する必要があるのか」 ということになりそうです。実際、JAVAで実装されたプログラミング言語は多々あります。

  • JRuby(ruby)
  • Rhino(javascript)
  • Jython(python)
  • Kawa(scheme)
  • ABCL(common-lisp)
  • JBasic(basic)

などなど...

で、「なぜJAVAなのか」と聞かれたら「便利だから」と答えたら身も蓋もないし… もうちょっと無い脳みそ振り絞って「何が便利なのか」を考えてみると、

  • 特定のプラットフォームに依存しなくなる
  • JAVAとLISPのコード間で双方アクセスが可能になる
  • 総じてLISPコード資産をJAVAで使える

こんなところでしょうか。("LISP"は各自他のプログラミング言語に置き換えてください)

他にもあると思いますが、自分がパッと思いつくのはこれくらいです。

2009年3月11日水曜日

Slimy hackathon #1 に参加してきた

g000001さん発案のSlimy Hackathonにちょこっとだけ参加してきました。
ハッカソンとはいえ連絡手段はLingrとSkypeだったので、ほとんど部屋に篭りきり生活でしたが(笑)

自分が行おうとしたタスクは2つ。

#### SLIME⇔Elispのコマンド対応

Common LispもEmacs Lispも元はMACLISPが祖先らしいから、CLの便利関数はきっとelispにもあるに違いないと考えたが始まり。CLのといいますか、SLIMEのコマンドですが。

で、出来たのがこれ → http://gist.github.com/75589

もう少しSLIMEに達者になっていればslime-who-callsとかslime-list-callersに対応するelispも探せたんじゃないかなと反省してます。とりあえずelisp-macroexpand-1は作ってみて結構便利なんじゃないかしら。

やっぱりelispだってLISP-2だしマクロ使えるしリスト遊びはできるし、LISPを学ぶには悪くないと思うなあ(あんまり深い再帰だと怒られるのが面倒だけど)。

#### newLISPをSLIMEから動かす

newLISPはどちらかといえばSchemeよりのLISPなんですが、swank-mit-scheme.scmなどScheme専用のswankサーバもあるらしい。「それならnewLISPでもswankサーバが建てられるんじゃね?」と思い立って基礎知識0から調べたらやっぱり完成はしませんでしたorz さすがに2日間はきつかった…

とはいえここで言い訳するのもアレですし、手ごたえが皆無というわけではなかったので、せめてnewLISPとSLIMEで通信が出来るくらいには完成させようかなと思います。出来るかな。


自分はこんな感じで、非常にまったりと参加させてもらったSlimy hackathonでしたが、結構楽しくやらせてもらいました。
あとSkypeではゆんゆん電波を飛ばしていた気がします。すいませんでした。

2009年3月3日火曜日

読書感想文#2

Shibuya.lisp テクニカルトーク #2」が無事に開催されたようです。

自分はニコニコ動画から見させてもらいました。 →http://www.nicovideo.jp/mylist/11305224

個人的には処理系/性能について語るのもいいけど、それから何か作ってみた系 の発表が面白かったかな。やはり動くものは見ていて楽しい。

特に印象に残ったTOP3

  • okuoku氏: SchemeでBluetoothスタックを書く Scheme(mosh)で外部プロトコルの処理をしようとする試み。 Wiiリモコンと通信するデモに思わず感嘆の声。 FFI(Foreign Function Interface)とは違うんだろう、たぶん。

  • 和田英一氏: 私がLispでプログラムを書く理由 楽しいから←結論 本当に楽しそうに話すなあ。 ダメ学生の自分はPostscriptプログラム紹介の後半少しだれてしまいました、ごめんなさい。

  • 林拓人/takuto_h: Cyanの現状と、これから 高校生が作った実験的言語Cyan(サイアン)の紹介。 「言語は本来、プログラムを書くためのもの。しかし、私は言語処理系しか書けない。」 彼に限らないが、子供の頃からプログラミングに慣れ親しむ環境があるってうらやましいなー。

一通り動画を見終わって改めてタイムテーブルを見ると、CLの発表者は少なかったんですね。(2人?)

残念ながら中継/動画配信されなかった数理システムの黒田氏の発表も、他の 「TT#2参加したよブログ」を見る限りかなりパンチのあるものだったらしい。 自分のような似非CLerがそれを聴いたら、きっとその場で畏縮してしまうんだろうなあ。。

ともあれ、動画を見てLISPに対するモチベーションが上がった気がしますね。

発表者の皆さんも運営の方々もお疲れ様でした。

2009年2月21日土曜日

今日の無題

地方県民はUSTREAMで我慢すればいいや、と思っていた結果がこれですか。。。ぐはぁ(泣)
今回の発表のUSTREAMでの中継/動画アップロードにつきまして 前回のTT#1では、発表者全員より撮影許可を頂けましたので、内容全編を中継/アップロード致しましたが、今回は、すべての発表者の方に撮影承諾を頂けておりませんので、一部内容の中継/アップロードができません。 Shibuya.lisp TT#2 ライトニングトークの発表内容決定!

2009年1月18日日曜日

[PCL]第29回CL勉強会セルフ反省会[CADR]

反省会というより、単なるログです。

  • PCL第4章 特に疑問はないので省略
  • Lispマシンエミュレータの話題 第29回CL勉強会CADR Lispマシンで遊んでみよう篇の準備物 からエミュレータ本体とディスクイメージを落とす 今回はchaosサーバは使わない エミュレータは実行形式で配布されているのでコンパイルの必要なし(ソース同梱) Winバイナリもあるけど、色々不便らしいので今回はLinux版を使う 以下エミュレータ起動までの手順
    [~/lisp/]$ ls
    chaos-cadr.tar.gz
    disk.img.tar.gz
    usim.tar.gz
    [~/lisp/]$ tar fxvz usim.tar.gz
    [~/lisp/]$ tar fxvz disk.img.tar
    [~/lisp/]$ cd usim
    [~/lisp/usim]$ ln -s ../disk.img disk.img
    [~/lisp/usim]$ ./usim
    起動まで50秒弱 [Intel celeron M, 1.40GHz] 起動後に現在の日時を聞かれるので"月/日/年 時:分"のように答える
    ***CADR [Running]***
    Please type the date and time: 1/17/81 22:20
    Time is Saturday the seventeenth of January, 1981; 10:20:00 pm, OK? Yes
    This is band 1 of CADR diskmaker image.
     System     78.48
     Zmail      38.5
     Tape        6.5
     LMFS       21.34
     Symbolics   8.13
     Microcode 841
    Distribution Unknown, with associated machine UNKNOWN.
    _
    後はここからEmacsの*scratch*バッファよろしくS式を書くらしい
    見つけたキーバインド
    F2-f: ファイルシステムエディタ起動
    F2-l: Listener(最初の画面)に戻る
    F2-e: Zmacs起動, (ed)
    F2-m: Zmail起動
    キーボードは101配列、106配列がごっちゃになっていた (なぜ?)
    ---106配列
    1234567890-^\
    qwertyuiop@[
    asdfghjkl;:]
    zxcvbnm,./\
    
    !"#$%&'()~=~|
    QWERTYUIOP`{
    ASDFGHJKL+*}
    ZXCVBNM<>?_
    
    ---Lispマシンエミュレータ
    1234567890-^\
    qwertyuiop@[
    asdfghjkl;:]
    zxcvbnm,./\ (backslash)
    
    !@#$%^&*()_^|
    QWERTYUIOP@{
    ASDFGHJKL::}
    ZXCVBNM<>?|
    見つからなかったキー quote('),doublequote("),backquote(`),plus(+),tilde(~) 解像度が1024x768だと縦幅が足りない… サイズの変更も出来るらしいんだけど、挫折(泣)
Lispマシンエミュレータ起動画面

2008年12月15日月曜日

(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月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月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月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 "はろー")))
"ーろは"

さーて、何をしようか。

ないないない

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月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月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にあれば尚良いんだけど…