Archive for 7月, 2008

Common Lispと改行コード

水曜日, 7月 30th, 2008

CLでread-lineを使ってファイルを読み込んでいるとき気づいたのですが、
read-lineは

改行によって終了された行を読む。
これは行を文字列として返す。
(改行文字を含まない。)
(CLtL2日本語版第2版より)

という風に説明があったのですが、
#\Linefeed(LF)は読み飛ばしてくれるものの、
#\Return(CR)が思いっきり残っていました。
ストリーム開くときに:external-formatみたいな感じで、
改行コードも指定できたりしないんですかね。
読み込むときは改行コードの判定処理をする必要があるのであれば、
結局自力でなんとかしないといけないとは思いますが、
書き込むときは~%一発で済ませたいものです。


そうそう、リトルバスターズ エクスタシーですが、
プログラミングをしたいという欲求に負けてしまい、
現在、朱鷺戸さんに殺されかけるところまでしかやってません。
それにしても、朱鷺戸さんのBGMがKeyの過去のアルバムにあった
「駆ける」だったのが一番驚きました。

with-output-to-stringと文字コード

日曜日, 7月 27th, 2008

Common LispでWebアプリのお話。
POSTで日本語(UTF-8)を含んだ文字列を送信すると、文字化けしてしまったので、
原因を調べてみると、URLデコードを行っている関数が犯人のようでした。

(defun url-decode-string (s)
(with-output-to-string (out)
(do ((i 0 (1+ i)))
((>= i (length s)) out)
(let ((c (char s i)))
(case c
(#\% (setf c (code-char (parse-integer s
:start (+ i 1) :end (+ i 3)
:radix 16 :junk-allowed t)))
(unless (null c) (write-char c out))
(incf i 2))
(#\+ (write-char #\Space out))
(otherwise (write-char c out)))))))

outの文字コードがしていされていないのが原因だと思うんですけど、
with-output-to-stringでは:external-formatキーワードは指定できないようです。
ストリームを開いた後に文字コードをしている方法はないかと調べてみたら、
system::set-stream-external-formatなる関数があったのですが、
string-streamには使用できないみたいです。
色々苦心してみましたが結局解決策が見つからず、
一旦バイト列に入れてから文字列に変換することにしました。

;;; CLISP only
(defun url-decode-string2 (s)
(let ((arr (make-array (length s) :element-type '(mod 256))))
(do ((i 0 (1+ i))
(j 0 (1+ j)))
((>= i (length s))
(ext:convert-string-from-bytes arr charset:utf-8 :end j))
(let ((c (char s i)))
(case c
(#\% (setf c (parse-integer s
:start (+ i 1) :end (+ i 3)
:radix 16 :junk-allowed t))
(unless (null c) (setf (aref arr j) c))
(incf i 2))
(#\+ (setf (aref arr j) (char-code #\Space)))
(otherwise (setf (aref arr j) (char-code c))))))))

ext:convert-string-from-bytesという関数がバイト列を文字列に変換してくれます。
バイト列に文字コードを指定して文字列を直す関数があるのは非常に便利ですね。
残念ながらCL標準ではないようですが、大抵の処理系にはこの手の関数があるようです。

試験とプログラミングは2つで1つなの

水曜日, 7月 23rd, 2008

*思えば中学1年の2学期の中間試験からだった*
プログラミングが妙にはかどるな、と思ったら試験間近なんですよ。
むしろ試験間近だからはかどるのかもしれません。

ひょとして今って試験直前?
以下の項目が3つ以上当てはまれば試験直前です。
1. プログラミングが妙に楽しい
2. プログラミングが妙にはかどる
3. 今作っているモノの今後の見通しがたっている
4. 勉強の今後の見通しはたっていない

試験がいつあるのか分からなくなってしまった方は、
ぜひ上のチェック表をご利用ください。
CLでWebアプリな話
現在、Lispサーバは、
1)ソケット作る
2)accept
3)処理
4)2に戻る
というごくごくシンプルなコードになっているんですが、
ご覧のとおり、同時に1リクエストしか処理できません。
やっぱし並列化すべきですかね。
CLでマルチスレッドってmpライブラリとかいうのを使えばよかったんだっけ…
あと、並列化すると、ファイルの排他制御とかが必要になりますが、
CLってそこらへんの機能を備えてるんでしょうか。
それにしても、セッション管理とか作って、せっかく楽しくなってきたのに、試験ですよ。
ほんと、プログラミングが楽しくなるといつも試験が目の前に。
一体どうなってるんでしょうか。
*ブログの過去のエントリーをあさって見た*

そろそろ、スクリーンショットや体験版を出したいんですが、もう少し時間がかかりそうです。
インタプリタ周りを触ってたらシナリオの方が全く進んでなかったりします。
試験前ですが、勉強も全く進んでません。
(2007/01/29 魔法の呪文はリリカルなの?)
全くもって使い道のないものをつくってしまいましたが、
大学の試験が近づいていることから逃避することによって発生するエネルギーを、
プログラミングによって発散した結果、生まれてしまったかわいそうなプログラムなので、
どうか生暖かい目で見守ってください。
(2008/01/25 PostScriptの上のLispの上のPostScript)
       ____
     /      \
   /  _ノ  ヽ、_  \
  /  o゚⌒   ⌒゚o  \  もう試験間近だお…
  |     (__人__)    |  きっと単位沢山落とすお…
  \     ` ⌒´     /
(2008/01/27 やる夫がgauche.nightの第1部座談会に出演するそうです)
       ____
     /⌒  ⌒\     これは神のお告げに違いないお!
   /( ●)  (●)\    試験なんか捨てて、新たにLispインタプリタを
  /::::::⌒(__人__)⌒::::: \   作れという神の意思なんだお!
  |     |r┬-|     |  
  \      `ー’´     /   
(2008/02/01 やる夫が変な夢をみたそうです)
ニコニコ動画のスクリプト『ニコスクリプト』の話。
試験期間中というのもあって(*)、こういうものを作ってみました。
(*)試験期間中は逃避エネルギーによってプログラミングの効率は普段の三倍まで上昇する
(2008/02/01ニコニコ動画で三目並べ)
明日でようやく試験も終わりです。
なので、こんなものを作りました。
(2008/02/05 ニコニコ動画でシューティング)

・・・やっぱし試験前はプログラミングの神様が私の元に降りてきて、
「試験はいいから。プログラミングをやりなさい」
と囁いているようにしか思えません。

サーバサイドでCommon Lispを使いたい!

日曜日, 7月 20th, 2008

今日は風子の誕生日ですね。
風子、誕生日おめでとー。
まあ、それは置いといて、
サーバサイドでCommon Lispを使いたい!

*サーバでCLの処理系を動かす*
まず、どうやってCLの処理系を動かすか、少し考えてみました。
1. CGIとしてCLを走らせる (恐ろしく遅い予感)
2. 何か専用のモジュールとか使う
3. 頑張って何とかする
で、調べてみたら、mod_lispというものがあるそうです。
FreeBSDならportsから簡単に入れることが出来ます。
これは、Apacheにきたリクエストを「Lispサーバ」に渡し、Lispサーバからのレスポンスを
元のレスポンスとして返すものです。Reverse Proxyのようなもの?
この「Lispサーバ」は、mod_lisp専用のプロトコルを話すCLのプログラムです。
これは、Apacheと同一マシンで動かしても異なるマシンで動かしてもいいようです。
ここで、少し疑問。
mod_lispという名前の割には全然Lispが関係ない気がします。
なんというかLisp『でも』使えると言った感じが…

*CLで書かれたWebアプリケーション*
CLで書かれた便利なものはないかと探してみたら、
CL-Wikiと、CLikiというものを発見。
CLikiは色々出来る半面、導入が面倒な(気がした)ため、CL-Wikiを使ってみました。
これは、Wikiだけではなく、HTTPサーバの役割も果たすようです。
上記サイトからCL-Wikiのソースをダウンロードし展開し、CLISP上で
> (load “start.lisp”)
とすると、ポート8080にCL-Wikiのサーバが立ち上がりました。
試しにブラウザから127.0.0.1:8080にアクセスすると無事表示されました。
次に、Apacheと共存させるために、mod_lispを入れた後に、http.confに次のような内容を追加。

#lisp
LoadModule lisp_module /usr/local/libexec/apache22/mod_lisp2.so
LispServer  127.0.0.1 8080 "cl-wiki"
<Location /lisp>
SetHandler lisp-handler
</Location>

これで、127.0.0.1/lisp/にアクセスがあると、リクエストがCL-Wikiに飛ばされますが、
このままでは動作しません
最初に書いたとおり、mod_lispは独自のプロトコルを使うため、
CL-Wikiの話す言葉をHTTPからmod_lispのものに書き換える必要があります。
とりあえず、必要そうなところを適当に書き換えたところ、
部分的ではありますが、無事CL-Wikiが動作しました。
ところで、勝手にソース書き換え単ですが、CL-Wikiのライセンスってどうなってるんでしょうか。
調べてもどこにもライセンスが書いてない気が…

*誰かエロい人教えて*
ちょこっと遊ぶ分にはうまく動いたようですが、
実際に外部に公開する場合、このやり方で大丈夫なんでしょうか。
CLを使ったWebサービスってどうなってるんだろう…
(AllegroCLのあれこれを使うっていうのは無しで(^^))

(追記)
CL-Wikiの作者のonjoさんにCL-Wikiを自由にあれこれする許可を頂きました。
ありがとうございます。

OSC行ってきた2。

日曜日, 7月 20th, 2008

懇親会で沢山ビール飲めて満足した。

OSC行ってきた。

金曜日, 7月 18th, 2008

OSCに行ってきた。
K*BUGさんのブースで初音ミクが動いてた。
楽しかった。

BSDで制御してるらしいです。
あと、なでしこの中の人とお話した。
なでしこは日本語の識別子を許すけど、
「から」や「へ」がキーワードとなっているので、
『おから』のような識別子が使えないそうだ。
おからに関するプログラムを書く機会がどれほどあるかは分からないが、
個人的には非常に悲しくなった。おからは美味しいのに…

CLでプログラム書いた

月曜日, 7月 14th, 2008

最近少しずつですがCLの勉強を続けていますが、
本やらを読むだけで一向にプログラムを書いていなかったんですが、
突如CLのプログラムを「書かなければならない」という珍しい状況におかれ、
ようやくCLでそこそこの規模のプログラムを書きました。
*覚えたこと*

(with-open-file
...
:external-format charset:euc-jp)

このように書けば文字コードを指定してファイルを開くことが出来ます。
このサイトなんかが参考になりました。
*勉強の効果(?)*
今まで、なるべく代入を使わないようにして、
再帰を沢山使ってプログラムを書いていたんですが、
今回はsetq, do, do*, dolistを多用しました。
無駄にかっこつけてもしかたないですしね(Lispなのに…)。
doマクロのなかでいくつか変数つくってsetqで代入とかやってたら、
なんだかCのプログラム書いてるのと似たような気分になりました。
そんなプログラム書いてたら、
時折出てくるmapcarが凄くいいものに見えてきました。
やっぱり簡潔に書けるっていい。
しかし、残念だったのが勉強した手のloopマクロを一切使わなかったことです。
やりたいことはdoやdolistで十分できましたし、
そもそもloopマクロで知ってる機能が少なすぎるのかもしれません。
……というか、単にループと代入を沢山使ったプログラムを、
勉強の効果と呼んでいいのやら(笑)
*マクロが便利だけど疲れる*
40行程度のマクロを作ったら、物凄く疲れました。
もっと細かく分割した方がいいのでしょうかね。
まあ、疲れた分、あとでずいぶん楽できたのでよかったです。


*どうでもよくないこと:*
前のエントリーで「ひだまりスケッチ365」とか出力してしまいましたが、
正確なタイトルは「ひだまりスケッチ×365」でした。
正直,スマンかった(AA略

ゆの in ニコスクリプト

金曜日, 7月 11th, 2008

世界中で数人しか知らなさそうですが、
ニコスクリプトは演算子オーバーロードが出来るんですよ!

0::/t=drawText(text:"",size:20,color:0xffffff)
1::/echo=dt
1::/echo.setSlot('lessThan',def_kari('#:g3',t.text+=$1))
2::/trg=dt
2::/trg.setSlot('divide',def_kari('#:g2',t.text+="365";echo))
3::/X=dt
3::/X.setSlot('divide',def_kari('#:g1',t.text="ひだまり"+$1;trg))
4::/_="スケッチ"
5::/X / _ / X < '来週も見てくださいね!'

実行すると動画内時間5秒で「ひだまりスケッチ365来週も見てくださいね!」と画面に表示されます。
dtというのは本来画面に文字を表示する手続き(drawTextの略)ですが、
この手続きは新しい(少し特殊な?)オブジェクトを返します。
で、そのオブジェクトのスロットに'divide'とか'lessThan'といった名前で手続きを格納すると、
'/' や '<' といった演算子が使われたときに任意の手続きが呼べるという仕組みです。 ちなみに、手続きを作るdef_kariは第一引数に名前を要求するので、適当な名前をつけています。 「こうやったら出来るんじゃないか?」と思いつきでやってみたら案外出来るものですね。 ちなみに、ひだまりスケッチはマンガは読んでますがアニメは見てません。
そもそもスケッチブックの方が好きだし。
そんなことよりも問題なのはゆのさまですよ。ゆのさま。
ひだまりスケッチが流行って以来、「ゆのは」でググっても
「'ゆの' は」で引っかかってゆのさまばっかし出てくるんですよ。
もうやってられないんだぜ!
……と書いてから気づいたけど、今はそうでもなくなってるみたいです。
(まあ、やっぱり上位にひだまりスケッチ関連がきてますけど。)
前に検索したときに丁度ひだまりスケッチが流行ってたから上位にきやすかったんでしょうかね。

第7回 慢性的CL勉強会[予習]

火曜日, 7月 8th, 2008

気が付けばかれこれ2週間もエントリーを投稿していませんでしたが、生きています。
サイクロンゆのはな一号機(自転車)の後輪が壊れて修理に1万円取られたり、
戦国ランスにはまりすぎて廃人になったり色々ありましたが、一応生きています。


「The Common Lisp Cookbook」の「The Loop Macro
を読み終わったので適当にまとめておきます。
*全体として*
説明よりもソースコード嫁!
という感じでした。
冒頭で「最も文書化されていない(least documented)」と書きながら、
ここでも説明が少ないというのには笑いました。
*内容のまとめ*
Loopマクロとは:
– Common Lispの中で最も価値があり、もっとも文書化されていない。
Loopマクロの価値:
(mapや再帰と比べて)
– 強力
– コンパクト
– 読みやすい
Loopマクロを成す4つの部分:
– 変数の設定(これは繰り返される)の式
– 繰り返しを終了させる条件の式
– 繰り返しごとに何かをする式
– 繰り返しを抜ける前に何かをする式
*試してみた*

[21]> (loop for x in '(a b c 1 2 3) thereis (numberp x) do (print x)) A B C T [22]> (loop for x in '(a b c) thereis (numberp x) do (print x)) A B C NIL

thereisは直後に書いた式がnil以外になるとループを抜け、tを返すようです。
全ての繰り返しでnilになれば最終的にnilを返すようです。

[23]> (loop for x in '(a b c 1 2 3) never (numberp x) do (print x)) A B C NIL [24]> (loop for x in '(a b c) never (numberp x) do (print x)) A B C T

neverは直後に書いた式がnil以外になるとループを抜け、nilを返すようです。
全ての繰り返しでnilになれば最終的にtを返すようです。

[19]> (loop for x in '(1 2 3 a b c) always (numberp x) do (print x)) 1 2 3 NIL [20]> (loop for x in '(1 2 3) always (numberp x) do (print x)) 1 2 3 T

alwaysは直後に書いた式がnilになるとループを抜け、nilを返すようです。
全ての繰り返しでnilにならなければ最終的にtを返すようです。
これら3つがwhenとreturnを組み合わせたものというのは大体納得できるのですが、
最終的に返す値を決めるには、これら3つを使わない場合、どうやって指定するんでしょうか。

[31]> (loop for x in '(a b c) never (numberp x) collect x) *** - (LOOP FOR X IN '(A B C) NEVER (NUMBERP X) COLLECT X): ambiguous result: (SYSTEM::LIST-NREVERSE #:ACCULIST-VAR-4350) from COLLECT X T from NEVER (NUMBERP X)

上記の最終的に返す値の話が絡んでいるかは分かりませんが、
never等とcollectを組み合わせるのはまずそうです。

[32]> (loop for x in '(a b c 1) when (numberp x) return x collect x) 1 [33]> (loop for x in '(a b c) when (numberp x) return x collect x) (A B C)

こういう書き方なら問題ないようです。