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
# に変換する必要はないのかなあ