読者です 読者をやめる 読者になる 読者になる

Lambdaカクテル

DESCRIBING: Scala, Akka, P2P, Scheme, Political sth., etc.

部屋が散らかる話/理解・想像の及ぶ範囲と学習

随想

気付けば机の表面が見えなくなっている.どうも片付けは苦手で,油断すると台風が過ぎたような部屋*1になってしまいます.出した物をしまえばいいだけの話ですが,ちょっと見ておきたいチラシや,書くか決めかねている書類などが机のわきに積っていき,上から見ればまるで都市のよう.どうやら,それがいつまで使われるのかの見積りが下手なのかもしれません.今机には,じゅうたんのチラシ(鴨川を散歩 - Lambdaカクテル 参照)と,最近買ったXperia Compactと,それにスマホの座を食われたiPhone,リラックスサプリメント,ポット代わりにしている水筒,WindowsタブレットNFCリーダ,AVRISPmkII,有機ELのキャラクタモジュール,高校の友達にもらったティーカップ,ドンブリ,紅茶入れにしているクッキー缶,個包装の餅,メモ帳などがある.あ,壊れたマイクもある.

後片付けが苦手な性格は生まれつきで,あらゆる物の後片付けに失敗して育ってきました.宿題ももちろん忘れるタイプで,当時はメモするという概念もそれを見返すという概念も存在しませんで,ガンガン忘れていました.よく会社員になれたなと思います.皆様ご迷惑をおかけしました.生きているうちに自分なりの対応方法を獲得した結果なんとかまともな生活ができるようになったわけで,人間の学習能力は大したもんだと思います.学習した技術を挙げてみると,しなければならないことが発生したときはすぐにメモを行い,遅滞なく最後まで完了させる(先に延ばさない)ということに尽きます.あたりまえのことですが,これができなかったうちは大変でしたが,自分で困った中で編み出した知見なので,守り続けられています.

僕は教えられたことをそのままやらずに余計な事をするタイプです.これをやれと言われても,納得するまで手を付けませんし,納得しない事には全く興味が持てずにむしろ疑問視するという感じです.

高校では,それを裏付けるように得意な教科と不得意な教科がありました.暗記系はさっぱりダメで,数学も「何になるのかイメージがわかない」という理由で上達しませんでした.地学といった自然科学は想像やアナロジーの働く余地があったので得意でした.英語も,「異言語で同じメッセージを表現できるのは面白い」という理由で得意になりました.このあたりは非常にピーキーで,数学の成績は常に学年最下位レベル(センター試験で勝手に選択したプログラミングで生き長らえたといっても過言ではありません)で,その一方英語はかなり上位に食い込んでいました.地方の進学校だったのでカリキュラムのスピードが速く予備校にも行っていなかったという弱点と,理解の堅実な積み重ねを必要とする数学との相性が最悪だったようです.今となっては,好きなところから数学を学ぶことができるので,数学がもっと好きになりました.実は数学は中学校までは得意でした.

学習では,理解していることが最重要だと思います.知っているところから押し広げていき,それを補強していくのが王道でしょう.少なくとも僕はそういったやり方が最も得意です.そのためには基礎的な知識がたくさんあることが大事で,なんとなく直感的に知っていることが多いほど,学習の初期において有利だと思います.そこから学問は発展していくわけですから.

これと対照的なのが高校数学のカリキュラムでしょう.素朴な理解から敷衍していくのではなく,いきなり各分野の定義を教え込まれます.僕はこれに耐えられませんでした.中学校で習った数学との連続性が感じられず,断絶した孤独な知識としか感じられませんでした.経緯というものを教えてもらえないのだから仕方ない話です.

最近は数学ガールを読んでいます.

*1:自衛隊で行われる教育的指導としての「台風」については, http://www.mod.go.jp/pco/saga/rensai/no_8/no_8.html を見よ.

ふさぎがちな時(ふわふわした暮らし)

随想

生きているとふさいだ気持ちになってしまうことがあります.これは事故のようなもので,ふさいでいるから人と関わりたくないのか,人と関わらないからふさいでいるのか,よく分からない穴に心がすとんと落ちてしまうことがよくあります.穴に落ちているので,意識が写し取る身の周りの情景はどこか虚ろで,それによって活動的な意識の手足はもがれてしまい,心身が防御体制をとりがちになるようです.

僕はたまに散歩に行ったり,人に愚痴を聞いてもらったり,まいっかと思ってゴロゴロしたりして,穴から抜けるのを待つことにしています.いつもの家のいつもの部屋じゃない環境にしばらくとじこもって,自分本位になるわけです.そうしていると,自分の心のたもとがゆっくり広がっていって,心が穏かになるようです.

穴に落ちているときにやらないようにしていることもあります.たとえば,SNSに接触しないようにしています.SNSでは他人の体験しか見られないから,自分と人をくらべてしまいます.人とくらべるとつらくなるだけです.自分の体験を持つことが大事かもと思います.僕はすぐ思い上がるので,画面で見たことのあるものや人を知った気になって,なにか別の物に気をとられてしまいがちです.でも,自分が知らないことはずっとたくさんあるわけです.それを知るのに,ちょっとした散歩や,人に相談すること,カフェに行くことは丁度良いようです.

自分を追い込まないように,ふわふわと暮らしていきたいですね.緊張も努力も安心も,適度が一番ということです.ふわふわ.

windymelt.hatenablog.com

社会人の読書と価値と遊びにまつわるエッセイ

随想

あまり共感してもらえないかもしれませんが,本の存在を察知して吐き気を催すことがあります.とくに,読まないまま机の脇に置いたままになっている「積ん読」のことを思い出したときがそうです.せっかくお金を出して手に入れた書籍を役立てること無く埃を被せている自分への不快感,もの言わず威圧的に居室の一角を占める紙の束が,まるで怒って牙を剥いているようです.

もっとも,書籍というのは他の媒体と同様,必ずしも自分にとって役立つものばかりではありませんし,役立てなければならないというものでもありません.しかし,ウェブサイトやテレビ番組といった他の媒体と比べるといささか,読み手に与える心理的プレッシャーは大きいようです.殊にそれが自分で買ったものであるときはなおさらです.自分がお金を出して買ってきたという現実が,ページという目に見える単位をめくるたびに,自分の心に重くのしかかってきます.できれば,小さい頃に絵本の読み聞かせをもっとせがんだ時のような,純粋に知的な好奇心に衝き動かされるままに本を読みたいのですが,社会人になってからというもの(あるいは,それを心のどこかで意識しだした学生時代のどこかから),まるで忘れ物を探すように本を読んでいる自分に気が付きます.

「社会人」という言葉は,「価値」という言葉と切っても切り離せない関係にあるように思います.人が誰かを社会人と呼ぶとき,社会が求める価値にみあった働きをせよ,と暗にその人は言っているわけです.ほとんどの社会人は価値の見返りに得られる賃金で生活しなければならないので,あらゆる所作に価値判断を差し挟まざるをえなくなります.僕の職域であるWeb系エンジニアは,価値に対する賃金の依存が強く(平たく言えば実力主義的で),価値を測るものさしの変化が比較的起こりやすい仕事です.このため,生活における価値判断の介入度合いも高まります.例えば,

  • この技術は役に立つのか?
  • この技術は残るか?
  • この知識は汎用性があるか?
  • この知識を知ることに意味はあるか?
  • これをすることは自分の価値を上げるか?

という半ば強迫的な自問自答に向き合うことを余儀なくされます.そしてコンピュータに関わる仕事をしている人はその多くがコンピュータ好きなので,プライベートでもコンピュータをいじって過ごしています.つまり,仕事とプライベートがシームレスにつながりがちで,例の価値判断という魔物がプライベートにも巣喰ってしまう土壌があります.僕は学生の頃から「ちゃんとした」人になろうという意識が高かったので,自ずと自己啓発書に引き寄せられていき,頭の中が「価値」に占められがちになってしまったように思います.

ところで,「価値」の対極は何だと思いますか? 僕は「遊び」なのかな,と思います.無価値を否定しない,何の役に立つかわからないものにも是と言う態度を僕は忘れがちです.生産性,キャリア,目的,立派といった言葉で窒息して身動きがとれなくなったことが何度もありました.その体験を通じてなんとなく分かってきたのは,「価値」という概念は,人間のありようのうち,ほんのわずかな箇所しか照らし出さないということです.だれにでも,価値という照明では陰になって見えなくなってしまう,重要で美しい色彩があります.そのことが,本をめぐる吐き気と向き合ってわかってきました.

価値判断の呪いからはすぐには逃れることはできそうにありませんが,そのような心理を作ってしまうことをできるだけ回避しようとする努力はできそうです.そもそもこの不安は,生きていけなくなるという不安に端を発していると考えられるので,不安に囚われないようにすれば自信が取り戻せると思います.そのためには,完璧主義を捨てることを考える必要があります.誰しも完璧な人は居ないので,自分がもし完璧にできなくても,そうそう世界から落伍してしまうことはないのだと信じるのです.自分と同じくらいの人だって沢山いるのだと分かっていれば,あまりみじめな気分にならずに済むでしょう.周りで立派な人々が活躍するような環境*1では,劣等感に苛まれるかもしれませんが,彼らとて完璧ではないはずです.自分を苦しめない程度の憧れが,最も人を幸せにすると思います.

書籍もまた人と同様に,完璧ではありえないはずです.本には無謬の威厳とでもいうべき佇まいがありますが,それも人が書いたものです.その証拠に,本には著者の名前が大書されているではありませんか.だから,ぶ厚いページをまとっていたとて,怯えなくて大丈夫です.本が本としてあるのは,それ自体に価値があるからではなく,それに書かれていることを皆が好んでいるからです.嗜好の問題だと思えば,本に気負うことはなくなるでしょう.紙に印刷されているのは,たまたまそうなっているのだと僕は思うことにします.

考えたことは以上です.考えていることを文字にしていけば考えが明瞭になるかと思い,なるだけ書き直しをせずに,慎重にフレーズにしていきました.書いているうちに不安が遠のいていくような思いがしました.今ならあの本と手をつないで,遊びにいけそうな気がする.

*1:今の職場がまさにそうです

SMBD - .localドメインを使うとネゴシエーションが低速化する

samba zeroconf mdns

表題の通りです.

LAN内でsambaサーバを運用していたところ,接続時に数秒以上待たされるという事象が毎回発生していました.この原因がおおよそ分かったのでメモしておきます.

TL;DR

  • 何が起こるか分かっていないなら,.localドメインを使うな

本編

まずWiresharkでパケットキャプチャを行ったところ,速度低下の原因がmDNSにあることが判明しました. mDNS(Multicast DNS)とは,DNSサーバを介さずにホストの名前解決を行う機構です.

mDNSが支えるZeroconf

mDNSを説明するために,まずはmDNSが支えているZeroconfについて説明します.Zeroconfとは,ケーブルをつなぐだけで設定無しにIPネットワークに接続できるようにするための一連の技法の事を指します.自動的にIPアドレスが付与されるDHCPもZeroconfを支える技術です.

DHCPを使うと事前情報無しでIPアドレスを使えるようになりますが,同じように名前解決も事前情報無しで行おうというのがmDNSです. DHCPではDNSサーバの情報が配られる事が多いですが,DNSサーバ情報が受け取れない場合もあります.mDNSはそのような状況をカバーするものだと言えるでしょう.

.local ドメイン

さて,mDNSが利用できる時にmDNSが常時使われるかというと,そうではありません.mDNSが名前解決を行うのは,標準ではTLD.localとなっているホストについて名前解決をしようとする時のみです*1.そうでない時は通常のDNSが使われます.そして今回,接続に時間のかかるsambaサーバのTLD.localに設定してあったため,名前解決にmDNSが使われていることがWiresharkによるパケットキャプチャの結果分かりました.そして,名前解決が待ち時間のほぼ全てを占めていることも判明しました.このような場合では高速に名前解決できれば問題は片付くでしょう.

OS X.localドメイン以外についてもmDNSを使う方法

OS Xでは,ネットワーク環境設定のインターフェイス設定画面にある「DNS」タブの「検索ドメイン」がmDNSに探索させるドメインの一覧です.ここに追加したドメイン.localドメインとがmDNSで探索されます.

他OSについても同様の設定があるはずです.

mDNSはhostsをバイパスする(っぽい)

低速化を回避するためにまず行ったことは,/etc/hostsIPアドレスとホスト名の組を記述し,静的に名前解決させるという対処でした.これによりmDNSやDNSが名前解決する前に静的解決ができ,速度の向上が見込めると思いました.しかしこの対処を行った後も,mDNSによる名前解決は続きました.確実なソースは未確認ですが,少なくともOS X 10.11.6(El Capitan) では,mDNSが使われる場合には/etc/hostsは使われないようです.

ドメイン.localdomainに変更 (解決)

クライアントのhostsファイルの変更では事態が改善しないため,sambaサーバのホスト名をmDNSに使われないドメインを含むものに変更したところ,mDNSは使われなくなりました.今回は.localdomainを使いました.

この変更によって,少なくとも接続時のネゴシエーションに時間がかかるというトラブルが解決しました.

smb.conf

今回の設定ファイルは以下のようになりました.

# $smbd -V
# Version 4.4.4

log file = /var/log/samba/log.%m
read raw = yes
load printers = no
write raw = yes
aio read size = 16384
socket options = SO_KEEPALIVE TCP_NODELAY SO_SNDBUF=16384 SO_RCVBUF=16384
username map = /etc/samba/user.map
interfaces = # interfaces #
encrypt passwords = yes
passdb backend = tdbsam
keepalive = 60
max xmit = 262140
netbios name = # HOST.LOCALDOMAIN #
cups options = raw
min receivefile size = 16384
server string =
max protocol = SMB2
min protocol = SMB2
aio write size = 16384
workgroup = # WORKGROUP #
os level = 20
use sendfile = true
security = user
max log size = 50
log level = 1
wins support = yes

参考文献

*1:このような動作をするのは,通常のDNSでしか名前解決できない場合の混乱を防ぐためです

言葉に出すこと

日記

上映開始の報を聞いてそのまま予約したチケットに起こされる形で眠い目をこすりながらたどり着いた映画館、の雰囲気はいつもワクワクする。

虐殺器官』。言葉と思考とテロリズムにまつわる哲学的な対話が何度も繰り返されるのは原作となった小説と同じだ。こういう考えさせられるのは好きなので、作者である伊藤計劃氏の早逝が悔やまれる。映画の情景描写が小説を読んで浮かんだ風景とあまり違わなかったのは監督の腕前か原作の描写の卓抜ゆえか。

最近脳が鈍っている。のは、何かを見聞きしたり会話をしても、その内容が頭に残らず、つむじ風のように消えて無くなってしまって本当にそこにあったの?と不安になるような時がよくあるからだ。作中では、〈思考は言葉に規定されるか?〉という一つの問いが発せられる。しかし私は言葉を使っているのか?普段生活していて言葉を使ったと意識した瞬間は何度ある?本当に言葉というものが現れるのは口頭で内省的な会話を行った時くらいのような気がしている。仕事は決まった形式の言語を使えばこなせる。世間話にも考える余地は大して無い。

持ち歩くのが嫌で最低限の金銭しか持ってこなかったおかげでラーメンを食べ損ねた。駅はいつも他人事のような顔をしている。地下鉄に乗った。

内省的な対話ができるのは幸せなことだ。それが喋る楽しみだと思う。どんなところが面白かったの?あれ良かったね。これは好き?どう思う?それはなんで?その問いかけはまっすぐ尖っていて、遠慮がない。親しい間柄の人間にしか許されない行為。世間話には出来ない鋭さ、人間の距離を天秤にかけず無視する危ない楽しみ。聞き上手な人は、その快楽を知っているのかもしれない。

クレジットカードは持っていたから、近場の回転寿司で食べてきた。明るいうちに食べるのは劇場と同じで、なんだか味気の無い諦めっぽい体験だった。外は雨の名残に石畳が黒ずんで光っている。

家で一人になりながらゲームをし時間を裁断していった後圧倒的にかき混ぜられてカクテルパンチになった頭では何も考えられないほど平たくなっていたが、独り言を言ってみた。頭の中の曖昧な部分に血肉が宿り、口から出て行く。それが新鮮で可笑しくてずっと一人で喋っていた。今まで喋らない生活をしていた。内省的な対話をしなかった。それは特権的な行為なのだけれど、人には必要なものだと思う。そこから親密さが生まれる。スマホ越しの文字の投げ合いやゲームの体験では得られない親密さだ。テーブルを囲んで向かい合った時に人は歩み寄ることができる。

今日もまた強くなった。

鴨川を散歩

日記

普段は夕方まで寝ているけれど今日は昼ごろ起きた。天気が良さそうだったのと、シャンプーを切らしていたのを買いたかったので鴨川に散歩に出かけた。

まだ微妙に寒かったが、陽が出ている間はぬくぬくとしていた。親子連れも老夫婦も出歩いて、大勢の人が好天を楽しんでいる。

鴨川はゆったりしている。水面を通った陽がさらさらした水底に光の網を作っていくのが好きだ。流れたり泳いだりする鴨の無為さが好きだ。そこだけ塗りつぶしたように広い、京都の中でもとりわけ広い空が覗くのが好きだ。こんな場所がもっとあればいいのに。

僕は出不精でまだあまり京都を出歩いたことがないものだから、普段よりまぶしい街中で色々なものを見つけた。その足で昼食をとり、薬局に行くまでの道をぶらぶらした。

京都には色々な建物があって、そのどれもが深みを感じさせる。傾き始めた陽を受けて文化の輝きを放っている。 正直なところ、ここまで楽しい散策になるとは思っていなかった。半ば無目的だったのが良かったのかもしれない。普段は大きな荷物を背負って歩くだけで、しなければならないことだけに意識が向いてしまうからだ。そうではなく、ただ鴨川をぶらぶらして立ったり座ったりする中で、人の息遣いだとか、社会の営みに触れられたのが楽しかったし、励みになったようだ。

道を南に下る。

途中でトルコ絨毯の店が小皿やオットマンを軒先に出しているのに目が止まった。赤い看板の下でしばらく立ち止まって躊躇していたけれど、広いガラス張りの向こうに薄暗く見える美しいインテリアに惹かれてなんとなく入ってみた。うちには絨毯もカーペットもない。 細密な文様が織り出された絨毯、モザイクをあしらった天井吊のランプシェード、手描きの絵皿。どこか奥ゆかしさの中にロマンスのある造詣は目と心の両方を愉しませてくれる。あいにく財布を持っていなかったのがかえって幸運で、また来る理由ができた。

gauche/schemeのparser.pegモジュールを使ってSlackの会話をパースした

gauche peg parser scheme

Gaucheのパーサジェネレータ parser.peg の使い方のメモ.

インストール

投稿時点の最新バージョンであるGauche 0.9.5ではparser.pegは標準添付されている.

Gaucheは拡張が豊富なことで人気のscheme処理系である.作者はハワイ在住の日本人エンジニアshiro kawai氏で,日本語・UTF-8に対応しており頼もしい.

$ brew install gauche
$ gosh
gosh> (use parser.peg)
#<undef>
gosh> ^D
$

基本的な流れ

  1. パーサコンビネータを利用してパーサを生成する
  2. peg-parse-string もしくは peg-parse-port を使ってパーサに文字列(ポート)を適用する
  3. パース結果が得られる

パーサの適用

(use parser.peg)
; TODO: implement parser...
(print (peg-parse-string PARSER "string")) ; => parsed result

peg-parse-port を使うと文字列ではなくポートに対してパースができる.

パーサの生成 (パーサコンビネータの使用)

コンビネータの詳細はソースファイルをあたることで参照できる.

以下,よく使うパーサコンビネータを紹介する.サンプルコードでは全て冒頭で(use parser.peg) を宣言しているものとする.

$char

ある文字にマッチし,その文字を返す.

コンビネータにマッチしないときはエラーとなる.他のパーサコンビネータでも同様である.

($char #\!) ; !にマッチする
(peg-parse-string ($char #\!) "!") ; => #\!
(peg-parse-string ($char #\!) "?") ; => *** PARSE-ERROR: expecting #\! at 0, but got #\?

$one-of

文字集合のうちどれか一文字にマッチし,その文字を返す.

($one-of #[a-z]) ; abcdefghijklmnopqrstuvwxyz のどれかにマッチする
(peg-parse-string ($one-of #[a-z]) "g") ; => #\g

$many

コンビネータの繰り返しにマッチし,リストを返す.最小文字数と最大文字数を指定できる.

最大文字数を超過しても,パーサはエラーを返さないことに注意.パーサは最大文字数に到達したことで満足し,文字列を読み取るのを止める.正規表現と同じである. 確実に最大文字数を守らせたいならば,コンビネータ eof を組み合わせるべきである.

($many ($one-of #[a-z])) ; a-zの繰り返しにマッチする
(peg-parse-string ($many ($one-of #[a-z])) "hoge") ; => (#\h #\o #\g #\e)
;; 最低5文字を要求する
(peg-parse-string ($many ($one-of #[a-z]) 5) "hoge") ; => *** PARSE-ERROR: expecting #[a-z] at 4, but got #<eof>
;; 5〜7文字を要求する
(peg-parse-string ($many ($one-of #[a-z]) 5 7) "hogera") ; => (#\h #\o #\g #\e #\r #\a)
;; 文字数を超過するとコンビネータは満足して探索をやめる.エラーにはならない
(peg-parse-string ($many ($one-of #[a-z]) 5 7) "hogerarara") ; => (#\h #\o #\g #\e #\r #\a #\r)

$seq

コンビネータが指示した順に並んでいるときにマッチする.順の最後のコンビネータの返り値を返す.

; $count: ちょうどその回数にマッチする
; digit: デフォルトで使用できるコンビネータ(文字集合ではないことに注意).
;        任意の数字にマッチする.
;        newline, space, spaces, alnumなどの仲間もある
; $c: $charのエイリアス
($seq ($count digit 3) ($c #\-) ($count digit 4)) ; 郵便番号にマッチさせる
(peg-parse-string ($seq ($count digit 3) ($c #\-) ($count digit 4)) "123-4567") ; => (#\4 #\5 #\6 #\7)

$do

$return と組合せて使う.マッチしたコンビネータから値を取り出し,加工して取り出す.

コンビネータを名前に束縛する際,コンビネータであることが分かりやすいように接頭辞%を付している.

(define %head ($count digit 3))
(define %tail ($count digit 4))
(define %postal
  ($do
    [h %head] ; %headが返す値がhに束縛される
    [dash ($c #\-)]
    [t %tail] ; %tailが返す値がtに束縛される
    ($return (list (list->string h) #"~dash" (list->string t))))) ; h, dash, tを利用して値を作り出す.$returnした値が$doが返す値になる
(peg-parse-string %postal "123-4567") ; => ("123" "-" "4567")

$lift

$do のモナディックなシンタックスシュガー.

; ($lift f parser) == ($do [x parser] ($return (f x)))
(define %head ($lift list->string ($count digit 3))) ; 3文字の数字にマッチし,文字のリストを list->stringに渡し,得られた値を返す
(define %dash ($lift ($ list->string $ list $) ($c #\-))) ; $はパーサとは関係ないマクロ[1]
(define %tail ($lift list->string ($count digit 4)))
(define %postal
  ($lift list %head %dash %tail)) ; %head, %dash, %tail それぞれの値が3引数としてlistに渡され,その結果が返り値となる
(define %postal-naive
  ($do [h %head] [d %dash] [t %tail] ($return (list h d t)))) ; 上掲を$doで書き直したもの

(peg-parse-string %postal "123-4567") ; => ("123" "-" "4567")
(peg-parse-string %postal-naive "123-4567") ; => ("123" "-" "4567")

$sep-by

デリミタとなるコンビネータで区切られた文字列にマッチし,デリミタを除いたリストを返す.

; $optionalは省略可能であることを示すコンビネータ.
(define %comma ($seq ($c #\,) ($optional space)))
(define %number ($lift ($ string->number $ list->string $) ($many digit 1)))
(define %comma-separated ($sep-by %number %comma))
(peg-parse-string %comma-separated "1, 2,3,4, 5, 6") ; => (1 2 3 4 5 6)

$alternate

おおむね$sep-byと同じ挙動だが,マッチしなかった場合の挙動が異なる.ちょっとこれは説明しづらい. マッチしなかったとき最後のデリミタの直前からバックトラック*1を行うので,デリミタが複数の意味で使われうるシチュエーションに適している. また,デリミタを捨てない.

(define %comma ($c #\,))
(define %period ($c #\.))
(define %word ($many upper 1))
(define %trailing-phrase ($do [us %word] [_ %comma] ($return (list->string us))))
(define %last-phrase ($do [_ space] [us %word] [_ %period] ($return (list->string us))))
(define %enumeration ($lift cons ($alternate %trailing-phrase space) %last-phrase))
(peg-parse-string %enumeration "VENI, VIDI, VICI.") ; => (("VENI" #\space "VIDI") . "VICI")

$between

2つのコンビネータに挟まれたものにマッチし,挟まれていた値を返す.

(define %number ($lift ($ string->number $ list->string $) ($many digit 1)))
(define %parenthesized-number ($between ($c #\() %number ($c #\))))
(peg-parse-string %parenthesized-number "(128)") ; => 128

$or

自明なので省略.他にも様々なコンビネータが用意されているので,コードを見て遊んでみてほしい.

試作 - Slack会話のパース

学んだことを形にしてみるために,parser.pegを使ってSlackのログパーサーを作ってみた.50行ほど.

ログはクライアントからコピペしてきた.ツールがうまく動かずに苦しんでいる様子が克明に記録されている.

github.com

(use parser.slack)
(print (slack-parse "windymelt [5:31 PM] 
ぽへー

[5:31]  
ちょっちQKしよ

windymelt [5:40 PM] 
ぽえ〜〜〜

[5:40]  
ぽよよよよ

[5:40]  
びー

windymelt [7:14 PM] 
ぽよ〜〜〜〜

[7:14]  
ぽぽぽぽぽ
"))
((windymelt
  (((5 31 PM) ぽへー)
   ((5 31 #f) ちょっちQKしよ)))
 (windymelt
  (((5 40 PM) ぽえ〜〜〜)
   ((5 40 #f) ぽよよよよ)
   ((5 40 #f) びー)))
 (windymelt
  (((7 14 PM) ぽよ〜〜〜〜)
   ((7 14 #f) ぽぽぽぽぽ))))

簡単にパーサが作れたね!util.matchと組み合わせたら加工もしやすそう.ここからの発展形として,HTMLに変換して出力するなどが考えられる.gauche.parseoptモジュールを使ってコマンドラインを処理すれば,手製のコマンドをパパッと書くことだってできそうだ.

参考文献

blog.shin1x1.com

*1:「後続のパターンがマッチしない場合に一つ前のパターンに戻って、別のマッチ方法を試行するのをバックトラック(backtracking)と呼びます」 パフォーマンスを意識して正規表現を書く - Shin x Blog