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標準ではないようですが、大抵の処理系にはこの手の関数があるようです。
Posted in プログラミング | No Comments »
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 ニコニコ動画でシューティング)
・・・やっぱし試験前はプログラミングの神様が私の元に降りてきて、
「試験はいいから。プログラミングをやりなさい」
と囁いているようにしか思えません。
Posted in 雑記 | No Comments »
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を自由にあれこれする許可を頂きました。
ありがとうございます。
Posted in プログラミング | 3 Comments »
7月 20th, 2008
Posted in 雑記 | No Comments »
7月 18th, 2008
OSCに行ってきた。
K*BUGさんのブースで初音ミクが動いてた。
楽しかった。
BSDで制御してるらしいです。
あと、なでしこの中の人とお話した。
なでしこは日本語の識別子を許すけど、
「から」や「へ」がキーワードとなっているので、
『おから』のような識別子が使えないそうだ。
おからに関するプログラムを書く機会がどれほどあるかは分からないが、
個人的には非常に悲しくなった。おからは美味しいのに…
Posted in 雑記 | No Comments »
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略
Posted in プログラミング | No Comments »
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は第一引数に名前を要求するので、適当な名前をつけています。
「こうやったら出来るんじゃないか?」と思いつきでやってみたら案外出来るものですね。
ちなみに、ひだまりスケッチはマンガは読んでますがアニメは見てません。
そもそもスケッチブックの方が好きだし。
そんなことよりも問題なのはゆのさまですよ。ゆのさま。
ひだまりスケッチが流行って以来、「ゆのは」でググっても
「'ゆの' は」で引っかかってゆのさまばっかし出てくるんですよ。
もうやってられないんだぜ!
……と書いてから気づいたけど、今はそうでもなくなってるみたいです。
(まあ、やっぱり上位にひだまりスケッチ関連がきてますけど。)
前に検索したときに丁度ひだまりスケッチが流行ってたから上位にきやすかったんでしょうかね。
Posted in プログラミング | 4 Comments »
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)
こういう書き方なら問題ないようです。
Posted in プログラミング | No Comments »
6月 23rd, 2008
Common Lispでは多値を必要としない場面で多値(正確には2個以上の値)が返された場合、
最初の値が使用され、後は捨てられます。
(list 1 (values 2 3))
=> (1 2)
ここまでは知っていたんですが、同様に多値を必要としない場面で0個の値が返された場合、
nilが使用されることが仕様で決まっていることを知りました。
(list 1 (values 2 3) (values))
=> (1 2 nil)
なんでもしっかり決まっているのがCommon Lispのいいところですね。
一方、Schemeはどうなっているかというと、
Schemeのvaluesは次のように定義できます。
(define (values . things)
(call-with-current-continuation
(lambda (cont)
(apply cont things))))
つまり、現在の継続に全ての引数を渡すという定義です。
これが正しく動作するには、現在の継続が複数の値を必要としているか(例えばcall-with-values)、
もしくは現在の継続が値を捨てる必要があります(例えばbegin)。
例えば、次のような式を考えて見ます。
(list 1 (values))
R5RSの形式的意味論では、これは「引数の数が異なる」というエラーになります。
R6RSの形式的意味論では、簡約が途中でとまってしまいます。
で、実際のScheme処理系で試したらどうなるか見てみました。
まず、DrScheme
(list 1 (values 2 3))
context expected 1 value, received 2 values: 2 3
> (list 1 (values))
context expected 1 value, received 0 values
R5RSの形式的意味論に忠実な感じです。
次にGauche。
gosh> (list 1 (values 2 3))
(1 2)
gosh> (list 1 (values))
(1 #<undef>)
gosh> (list 1 (values 2 3) (values))
(1 2 2)
gosh> (list 1 (values) (values 2 3) (values) 4 (values) (values 5 6) (values))
(1 #<undef> 2 2 4 2 5 5)
1つ目と2つ目を見た段階では、Common Lispとほぼ同じ仕様かと思いきや、
3つ目と4つ目がなんだか凄いことになってます。
これはバグ……というか仕様でしょうか。
Posted in プログラミング | 1 Comment »
6月 22nd, 2008
Lingrのログはこちら。
*疑問への回答*
今回もまた質問に答えていただきました。
いつも本当にありがとうございます。
– carを書き換えるnreverse
リストの要素の方を入れ替えれば可能。
HyperSpecにもcar/cdrのどちらを書き換えても良いと書いてあるそうです。
(format t "Test ~A ~:[FAILED~;passed~].~%"
',name (progn ,@body))
(format t ~:[にる~;てー~]~%” t) ;=> てー
(format t ~:[にる~;てー~]~%” nil) ;=> にる
だそうです。
,@(when condition
`((,condition () (format t "Test ~A passed.~%"
',name))))
,@ を , に変更して、 ((,condition …)) を (,condition …) に変更すると、
conditionがnilであるとき、この式全体がnilとして残ってしまいます。
@nilを使えば、@,nilとなってnilが消えるという仕組みです。
これはwhenの条件部がnilのときの値はnilになるということに基づいていますが、
whenの戻り値を使うべきではないという宗派もあるため、
ifやandを使うべきという意見もあるようです。
ちなみにschemeのwhenは条件部が偽のときの値は未定義だとか。
今回は勉強会後の雑談コーナーが一番熱かった気がします(笑)
*がんばって自分で調べてみた*
自分で投げかけた疑問を放置しておくのもどうかと思うので、
一部だけですが自分で調べてみました。
以下の内容は全てCLtL2日本語版を参考にしています。
– read-from-string
文字列を与え、それをリーダによってLispオブジェクトに組み立てて返す関数。
読み込まれなかった文字列の中の最初の添え字も返すとのこと。
– potential number(潜在的な数)
数の構文の拡張を可能にするために定義された構文。
潜在的な数であり、なおかつ実際の数の構文に合わないようなトークンは、
すべて予約語となり、処理系に依存した解釈となるそうです。
処理系はエラーを発しても良いし、シンボルとしてトークンを扱っても良いし、
あるいは何か他の行動をとっても良いとか。
まあ、使わない方が無難なようです。
– proclaim
宣言の効果をグローバルに持たす「関数」。
(proclaim ‘(type float tolerance))
を評価すると、toleranceの動的な値は常に浮動小数点数となるべきことを指定するそうです。
関数であるため、コンパイル時に効力を期待するなら、
eval-whenで囲むか、declaimを使うべきだとか。
– declaim
proclaimと同様のことを行う「マクロ」。引数は評価されない。
コンパイル時に宣言がなされることになるそうです。
—
毎週、何かしらの資料を読んで、
おまけに疑問に対する回答まで頂いてるため、
ここ数週間でかなりCLに対する知識が増えた気がします。
ただ、短いサンプルソースといったようなものしか読んでいないため、
本当にCLを使えるようになるためには、もっと実践的なソースを読んだり、
なにかモノを作ったりする必要がありそうです。
早くCLerになりたい!!
—
おまけ
# onjo
# いやいやよく考えてください、 ((((foo)))) こんな呼び出し嬉しくない。
# (funcall (funcall (funcall (funcall foo)))) 何回呼んだかすぐわかる。カッコの数を数えるなんて苦痛。
いやいやよく考えてください、
(funcall (funcall (funcall (funcall ;゚Д゚))))
こんな呼び出し嬉しくない。
(((( ;゚Д゚))))
顔文字であることがすぐに分かる。崩れた顔文字は苦痛。
なんてことを考えてしまったwww
Posted in プログラミング | No Comments »