LispとSchemeのねた。
Link
全部挙げるのは無理なので、このページで言及しそうなページのみ。
Lisp
Scheme
SKK Advent Calendar16日目の記事です。
プログラミングねたを書いてみます。
プログラミング言語を学習する場合、
ネットワークプログラミングは避けて通れません。
手始めに書くのはおそらくechoサーバでしょうが、
その次はどれを書こうかな、とそんなときにskkservはいかがでしょうか。
- 簡単なプロトコル
- かな漢字変換エンジンが簡単
- 実際に動くと楽しい
skkのプロトコルは単純です。
skkserv.cの先頭部分のコメントに入りきるくらい単純なものです。
それでいて、なにがあってもクライアントと接続を維持しつづけないといけない、
というあたりがなかなか微妙な感じで、
学ぼうとしてる言語の癖が掴めてよろしいのではないでしょうか。
また、かな漢字変換の仕掛けが簡単で、
ここではまることがまずありません。
モチベーションの維持によいです。
書いてみる
せっかくですのでuimで書いてみました。
uimも1.5.xまではそれぞれのimが各クライアント付属のライブラリを使って通信していたのですが、
いまではソケットまわりの低レベルな操作より上のレベルでは、
schemeで書かれた各モジュールが各プロトコルでサーバと対話するようになっています。
ネットワークの基本的な部分は汎用の部品が用意されているわけです。
さて、肝心の漢字変換ですが、
uimにはlookというモジュールがありまして、
これはlook(1)をそのままライブラリとして使えるようにしたものです。
正確にマッチするようにすると普通の変換、
先頭にマッチするすべてのエントリを列挙すると予測変換ということになります。
辞書はソートされている必要があるのですが、
便利なのでuimのいろいろなところで使われています。
というわけで、部品は揃ったので、
これらをうまくつないでやれば結構簡単に書けます。
コードはこんな具合です。
サーバの機能よりも辞書のパースの方がコードが長いくらいですね。
uim-sh /path/uim-skkserv
でport 1178で起動します。
辞書は~/.uim.d/dict/SKK-JISYOを使います。
ipv6も使えますし、予測入力も対応しています。
lookは単純に二分検索するだけですが、
手元の辞書500900エントリでも一瞬で出ます。
string-concatenate-reverseはuim-1.6.0に入れ忘れたものです。
ごめんなさい。
XMPPクライアントをまっさらのschemeで書くのに挫折したので、
かわりにloudmouthをchickenから呼べるようにした。
マニュアルはないけど、C APIを移しかえただけなので、
サンプルのloudmouthのコードそのまま持ってくれば動く。
オウムがえしするだけならこんな感じ。
コールバックの形式だけは普通にforeign-lambdaできないので、
ヘルパーマクロを使って、
(lm:define-message-handler
presence-cb
(conn mes data)
(begin ここにコールバックの内容))
(lm:call-with-message-handler
presence-cb #f #f
(lambda (handler)
(lm:connection-register-message-handler
conn handler
*LM-MESSAGE-TYPE-PRESENCE*
*LM-HANDLER-PRIORITY-LAST*)))
みたいに書く。
コールバックの第一引数がvoid*なものだから、
データの移送はかなり面倒なことに。
これをscheme-objectにすると、gcされちゃう。
これを使ったtwitter->xmppボットは安定動作しているようなので、
モジュール部分だけ公開してみる。
CLだとcl-xmppなんてのがあって、
asdf-installで簡単インストールできたりしてちょっと羨ましかったり。
あんまりにもさみしいのでコメントをつけられるようにしてみた。
自前で書くのも面倒なので、
DISQUSと言うサービスを使ってみることに。
friendfeedも公式に対応してるみたいなのでメンテも楽そうだ。
なかなかおしゃれな掲示板なのだが、
動作がかなり重い。新しいマシンが欲しくなるくらいだな、こりゃ。
chickenでスパムちゃんぷるーDNSBLをチエックするeggを書いた。
呼出しは(spam-champuru? host)だけ。簡単。
(use spamchampuru)
(spam-champuru? "192.0.2.1")
=> #t
(spam-champuru? "example.com")
=> #f
ここまできたら、やっぱりSchemeでudpmsgを書かないと。
これは今書いた。CheckenかわいいよChecken。
(use udp)
(define ipmsg-version #x0001)
(define ipmsg-default-port #x0979)
(define ipmsg-sendmsg #x0020)
(define (format-msg num host user cmd extra)
(format "~a:~a:~a:~a:~a:~a" ipmsg-version num host user cmd extra))
(define (send-udpmsg host msg)
(let ((sock (udp-open-socket)))
(udp-bind! sock #f 0)
(udp-connect! sock host 2425)
(udp-send sock (format-msg (random 16777215) "myhost" "myname" ipmsg-sendmsg msg))
(udp-close-socket sock)))
って、3つの中で一番読みやすいな。