昨日ついつい仕事してしまったので今日は休もうかと思いきや、いろいろとほっぽり出していた雑務とか新居の環境整備とか、いろいろあって結局あんまり休めなかったなあ。
ところで新居ではデスクトップマシンは無線 LAN にしないと配線が泥沼になって破滅しそうだったので今使ってる Gentoo マシンに無線 LAN 環境を構築したのだが、こっちの作業は別の意味で破滅していた。
俺が買ってきたのは Buffalo の WLI2-PCI-G54S で、こいつを Linux で使うためには b43-fwcutter というツールが必要になるようだ。そっちのインストールとかは特に問題なく終わり、あとはオフィシャルのドキュメントの通りにやってりゃいいだろうと思っていたが、何か wpa_supplicant だと上手くいかねえ。かといって wireless-tools は今のご時世に WPA に対応してないというのはなあ(こっちはあっさり繋がった)。原因の当たりは付いてはいるんだけど、今日はちょっと時間切れ。
なんかもー、引っ越したり退職したりすぐに働き出したりで、とにかくいろいろ目まぐるしく日々が過ぎ去る中、やんなきゃいけない事もやりたい事も多すぎてどっからアウトプットを出して行ったらいいかわからんので、とりあえず激烈にショボくてもいいから形になった物は公開していくことにした。
GAE 用の対話シェルを Google が公開しているんだが(http://shell.appspot.com/)、以下の点が非常に不満だった。
というわけで、その辺りを直してみた。最初はセッションの回収をスレッドを使ってやってみようかと思ったんだけど、そういや GAE はシングルスレッドでしかアプリを作れないので、これは没。しょうがないからアクセスの度に回収するようにして、これは何ともダサいけど仕方がないか。
仕事で StringTemplate の使用を検討しているので、こいつの仕様とかを調査してた。まず最初に断っておくと、俺は StringTemplate の開発者 Terence Parr の意見である「テンプレートは表示だけしてりゃいいんだよ。ビジネスロジックを書けるテンプレートエンジンとか死ねよ(意訳)」には全面的に賛成であり、早く PHP とか滅べとか思うし、未だに JSP でスクリプトレット書いてる奴は解雇あるいは切腹に処すべきだと思うし、テンプレートエンジンにビジネスロジックを書けるだけの機能を詰め込んでる開発者にはいい加減にしろといいたい(俺は大学の頃からこういうことを主張してた気がするけど、あんまり受け入れられた事が無かった気がする)。
というわけで根本的な思想には全面的に賛同した上で、ちょっと StringTemplate のアレな部分を1ヶ所指摘。といっても人によっては許容範囲かもしれないし、実際俺も完全に否定する物でもないんだが。とりあえずお馴染みの Hello, World。
import stringtemplate3
hello = stringtemplate3.StringTemplate("Hello, $name$")
hello["name"] = "World."
print str(hello)
こいつを実行すると "Hello, World." と表示され、それはまあ想像通りだろう。それでは、次のコードはどうだろう。
import stringtemplate3
hello = stringtemplate3.StringTemplate("Hello, $name$")
hello["name"] = "World."
hello["name"] = " Fuck you."
print str(hello)
こいつを実行すると "Hello, World. Fuck you." と表示される。つまり同一のアイテムへの代入操作がリストへの追加になっているわけだが、オイコラ Terence Parr、 a = b のような代入をした後で a == b にならねえのはバグの温床だろ、何やってんだ。これって C# のプロパティとかでも似たようなことができるけど、俺はこういうのはどうかと思うぜ。もしも似たような事を俺が考えるなら、まず次のようなクラスを作り、それをテンプレートの各プレースホルダで扱われる値とする。
class TemplateValue(object):
def __init__(self, initial_value):
self.data = [initial_value]
def __iadd__(self, value):
self.data.append(value)
return self.data
こいつの使い方はこんな感じ。
v = TemplateValue(1) # data == [1]
v += 2 # data ==[1, 2]
StringTemplate は __setitem__ を使って代入をリストへの追加にしてるんだけど、だったらこういうクラス使った方が良いと思うんだけどなあ。
とはいえ、ざっとドキュメントに目を通してテンプレートを書いたりした限りでは、他の不満点は繰り返しのキーワードが foreach とかそんぐらいだったんだが。致命的な弱点としてはデフォルトで HTML 特殊文字がエスケープされないことが発覚。これは StringTemplate があくまでも汎用的なテンプレートエンジンで、特に Web ページの出力に特化しているわけじゃないのが原因だろう。
追記:あれー、 foreach 構文が見つからないぞ!? 前にどっかで見つけたときはあったはずなんだけど、もしかして別のエンジンと混同してた?
追記2:違う、ある。ただ構文が超絶変態的なだけだった。 $val:{arg|value=$varg$}$ とか舐めてんのか。
最初は出力時のフィルターでどうにかならないかと思ったが、フィルターでは普通にテンプレートに書かれている文字列(=エスケープしちゃダメな文字列)も一緒くたに扱ってしまっているので、フィルターに渡る前の段階でどうにかしないとマズそうだ。ぶっちゃけ変数の内容を片っ端からエスケープすりゃいいと思うので、テンプレートに値を突っ込むところでどうにかすればいいだろう。というわけで自前のテンプレートクラスを StringTemplate クラスから派生させて作ろうかと思ったが、このテンプレートクラスは以下のような入力にも対応しないといけないらしい。
import stringtemplate3
hello = stringtemplate3.StringTemplate("Hello, $name$")
hello["name"] = ["World.", " Fuck you."]
print str(hello)
まだコードをきちんと追ってないので不明な部分があるが、結構複雑なテンプレートの構築にも対応しているようだ。そのため StringTemplate の該当部分のコードは思ってたより複雑で(といってもたかが知れてるが)、鼻クソほじりながら改造というわけには行かなさそう。まあこれは今日の午後辺りに手を付けようかな。
とりあえず StringTemplate についてさらに調査を進めたので、件の HTML エスケープ問題についての現時点での対処の方針などを書いておく。結論から書くと、 AttributeRenderer で どうにかなりそうではある。
まず最初に俺の考えたやり方というのは、 StringTemplate の Writer クラスを自前で作り(上に書いた中でフィルターと呼んでいる部分だ)、そこで HTML 特殊文字のエスケープを行うというものだ。その Writer のコードはこんな感じ(実装は適当)。
class HTMLWriter(stringtemplate3.NoIndentWriter):
def write(self, string):
value = string.replace('&', '&')\
.replace('<', '<')\
.replace('>', '>')\
.replace("'", ''')\
.replace('"', '"')
self.out.write(value)
return len(value)
ところがこれは上手く行かない。以下のコードを実行すると、残念な結果になってしまう。
html = stringtemplate3.StringTemplate("<p>$p$</p>")
html['p'] = '<>'
html.write(HTMLWriter(sys.stdout))
期待される出力は "<p><></p>" だが、実際の出力は "<p><></p>" で、つまりテンプレート変数に埋め込まれる値もテンプレート本体の文字列も、一緒くたに全部エスケープされてしまう。
こういう時に使うのが AttributeRenderer で、例えば問答無用に HTML 特殊文字をエスケープする素朴な実装はこんな感じになる。
class HTMLRenderer(stringtemplate3.AttributeRenderer):
def toString(self, o, formatName=None):
value = o.replace('&', '&')\
.replace('<', '<')\
.replace('>', '>')\
.replace("'", ''')\
.replace('"', '"')
return value
使い方はこう。
html = stringtemplate3.StringTemplate("<p>$p$</p>")
html['p'] = "<>"
html.registerRenderer(unicode, HTMLRenderer())
print html
formatName 属性をテンプレートに指定することでも制御が可能で、例えば日付のフォーマットぐらいはテンプレート側で指定できてもバチは当たらないだろうから、次のようなテンプレートと Renderer を考えてみる(全力で手抜き)。
class DateRenderer(stringtemplate3.AttributeRenderer):
def toString(self, o, format=None):
return o.strftime(str(format))
tmpl = stringtemplate3.StringTemplate('date1: $d1;format="%Y/%m/%d"$\ndate2: $d2;format="%m/%d"$')
tmpl['d1'] = date(year=2009, month=6, day=4)
tmpl['d2'] = date(year=2009, month=6, day=4)
tmpl.registerRenderer(date, DateRenderer())
print tmpl
出力結果は次の通り。
date1: 2009/06/04
date2: 06/04
というわけで、檜山さんの懸念である「div要素内とpre要素内とscript要素内では、エスケープ方法や改行の扱いが微妙に違ったり」という部分については、おそらく上記の format を用いてテンプレート側で指示を出し、それに対して Renderer 側で処理を分けるという方法で対処可能では、というのが結論ですかね。
また Smarty の修飾子や Django のフィルタとの関連性について書くと、実はこの Renderer が修飾子/フィルターの役割を果たしているというのが StringTemplate のアーキテクチャであり、「Renderer と修飾子の実行順序」については、実は両者は同じ概念なので気にしなくて良いのでは、という事になります。
ここ数日、午前中のうちに新居の掃除やら家具の組み立てやらをやって、午後から夜にかけて仕事とかやってたんだけど、流石に体力が追いつかなかったらしい。マジで働き方をもうちょっと真面目に考えないとダメだな。
とかいいつつも、今日はまだ目標に達していないから、こっからまだ仕事なんだが。
新しい机と椅子が届いた。机の方は組み立てにえらいことてこずったというか、マニュアルの通りにやると途中でハマるぞ。ハマる理由は一部のパーツに遊びが全然なかったり取り付ける位置がいやらしかったりすることで、特に遊びがないせいで嵌め込むのに苦労するパーツに付いては拳で殴って嵌め込まざるをえなかった。おかげで拳がイテエ。
明日はベッドが届いてネットが開通する予定なので、ようやく今のアパートから新居に生活拠点を移せる(今のアパートは今月までの契約)。あとこの日記を置いてあるサーバはいわゆる自宅サーバなので、明日か明後日ぐらいに一時アクセス不能になる予定。
ベッドが届いた。これでほぼ完全に新居で生活できるな。今のアパートではロフトで寝ていて、当然それは布団を敷いて寝ているわけで、ベッド生活は 25 年ほど生きてきてこれが初めてだ。
それにしてもここ二週間ほどはありえないぐらい忙しかったな。いろんなタスクが平行して動くとろくな事にならないのは重々承知してるけど、今回ばかりはそうもいってらんなかった。
今は仕事でも趣味でも分散 SCM を使っているのだけど、結局今は git と Mercurial の一騎打ちになってんのかな。どっちも最新の情報を追えているわけではないけど、マージングアルゴリズムとか今はどうなってんだろ。これは余裕があったら調べたい所だけど、しばらくはそんな時間は作れそうもないんだよな。
これまでの経験からして、 SCM はマージングアルゴリズムが賢かったり変更履歴の柔軟な管理ができないと(特にチーム開発では)使い物にならなくて、それもかなり無茶苦茶なケースに対して対応できないといけない。割とトホホなケースを出すけど、こんな感じの事故を俺は目にしたことがある。
この時に問題になってるのは二番目から三番目にかけての手続きで、この部分の変更履歴をなかったことにできる機能があれば問題は即座に解決できたはずだが、実際にはそういった機能のない SCM だったのでこの問題の解決には相当な工数がかかっていた。というか、そういった機能のある SCM って特に集中型の SCM ではないと思う。流石にここまでのケースは滅多にないだろうけど、マージが SCM を運用管理する上での重大なテーマであることは間違いない。
あと先にマージの方向が A→B で固定と書いたけど、これはリポジトリがフレームワーク・共通ライブラリ系と業務系の二系統で管理されていたからで、これがもっと細かくリポジトリを分けるポリシーだとマージは双方向に発生するし、その頻度も高くなる。つまり、マージに関する事故(コンフリクトとか)が発生しやすくなる。
なのでマージを可能な限り行わないとか(というか手動マージ)、リポジトリは一個しか作らないとかいうポリシーで運用されたりするんだけど、これはいろいろとマズい。特にマズいのは作業途中の状態でコミットしにくくなる事で、例えばどうしてもビルドやテストに影響のある状態でしかコミットできない場合、それをコミットしたら他のメンバーがリポジトリから最新のコードをチェックアウトしたときに悲劇が起こる。
このマージの問題に付いては分散 SCM の方が間違いなく上手く扱えると思っていて、中央のメインリポジトリでのコンフリクトや、コンフリクトが頻発するような状況をどうやって切り抜けるかのノウハウさえあるなら、もういい加減 CVS とか Subversion から脱却してしまってもいいんじゃないかと思う。
まあ SCM については変更管理との兼ね合いもあるので、SCM の方だけ見ていてもダメだったりするんだが。
昨日の話の続きというか補足。昨日例に出したトホホなケースを「ありえねえだろ」と思う人がいるかもしれないけど、これがありえる話だから困ってる人がいるんだよ。それにあそこまで酷い状況は稀でも、人間ってのは何かしらやらかしちゃうものだからな。こんなこと書いてる俺だってノーミスで作業できる自信なんか全然ないし、常にミスを未然に防ぐ体制作りができる自信もない。少なくとも、自分にとって未知の問題には備えようがない。
だからソフトウェアはフールプルーフに作られているべきだと思うし、開発ツールの中でも特に SCM はそうあるべきだと思ってる。下手をすると一人のヘマで(時に数百人規模になる)チーム全体が影響を受けるんだからな。ってか誰だってヘマした奴の尻拭いなんて嫌だろ。だったらコンピュータに尻拭いさせようぜ。
Python であるオブジェクトが特定の型かどうかチェックするには isinstance 関数を使い、その時に「これは関数型か」「これはクラス型か」などのチェックを行うには types モジュールを使う。ただし、その時はちょっと注意が必要なので以下覚書。
まず Python は 2.2 だか 2.3 だったか忘れたが object 型が導入され、クラスの形式に二通りの表現方法が混在している(これは Python 3.0 では新形式に統一)。
# 旧式のクラス
class Foo():
pass
# 新しい書き方はこう(3.0 からはデフォルトで object を継承)
class Bar(object):
pass
両者の違いの詳細に付いてはドキュメント読めなんだが、その一つとして isinstance したときの挙動の違いがある。
>>> isinstance(Foo, types.ClassType)
True
>>> isinstance(Foo, types.TypeType)
False
>>> isinstance(Bar, types.ClassType)
False
>>> isinstance(Bar, types.TypeType)
True
一体なんでこんな事になってるのかと言うと、昔の Python ではクラスと型が未整理で、統一されているとは言い難かったということがある。例えば 1 という整数リテラルの型を調べてみると、それは int 型(types.IntType)となり、さらに int の型を調べてみると type 型(type.TypeType)となる。つまり、
という階層ができている。ところが旧式のクラスについてはこの階層から外れており、先の Foo クラスのインスタンス foo を作った場合、その型を type で調べた時の戻り値は instance となり、そこから型情報を得ることができない。型情報は __class__ 属性から取得できるが、これはちょっと不統一でわかりにくい。さっきと同じように箇条書きにすると、
という割と終わってる混沌状態であり、 2.x 系統の Python でとあるオブジェクトが組み込み型、ユーザ定義クラスを含めた型オブジェクトかどうか調べるには、旧式のクラスも考えた上で以下のように書く必要がある。
isinstance(o, types.ClassType) or isinstance(o, types.TypeType)
こういうのを見てるとさっさと Python 3.0 に移行したくなるけど、でもしばらくは 2.x 系統との共存が続くだろうからな。
ANTLR の修行の真っ最中なんだが、確かにこれは良くできているよ。今まで事ある毎にパーサを自作したりしてきたけど、それはあくまでも単純な構造のデータが相手だったからな。ある程度以上複雑だったり、あるいは複数の言語でパーサを書かないといけない場合、それを全部手でやるのは難しい。
というわけで ANTLR の(多分)初学者向けチュートリアルを読みつついろいろと実験中。そういや ANTLR では LL(k) 文法だけが受容可能らしく、その LL(k) 文法というのは k 個のトークンを調べることで決定可能なものらしく、曖昧さのない文法といえるそうだ。
構文解析系の話はそれ単体でも結構面白そうだから、いつかもうちょっと真面目に取り組んでみたい気もするけど、まあ今のところは ANTLR を使えるようになるのが先決か。
マンションの近くの自販機で、なんかもう見た感じ破滅してる飲み物を見つけた。
かなり終わってる味がするので俺はきっとこいつを口にすることは金輪際ないだろうが、それでも世の中にはこの味が堪らないという人がいるかもしれないので、そういう人とは一緒にご飯を食べに行きたくないなーと思ったりした。
全然話は変わるけど、誰か俺のお古のギター要らない? ずっと前に買った安物の SG がどうも馴染めなくて処分しようと思ってたんだけど、どうせなら「安物でもいいからギター欲しい」という人に譲ろうかと思ってね。え、 Jackson Stars の方をくれ? なに寝言いってんだ。
というわけでギター本体とソフトケースにピックをいくつかと、あと amPlug の Classic Rock と Metal のセット。廃物処理の一貫としての事なので、お代は一切いただきません。
ボディには結構傷が付いてる気がするけど、目立たないのでまあ気にするな。
フィンガーボードはローズウッドだけど、 Jackson の方と比べるとなんか大分みすぼらしい気がしなくもない。まあ安物だしな。
というか廉価ギターの代名詞である Photo Genic なんだからそこはいろいろ察してくれ。一応フォローしておくと、まったく使えないわけじゃないというか普通に弾けるよ。ただ俺にはいろいろ合わなかっただけの事で。
あと宅急便で送るとか俺には敷居が高すぎるので、現物を手渡しすることになるかと思います。そのため東京か千葉かその辺に住んでる人でないと多分無理です。というわけで、ボロギターをタダで手に入れるためにノコノコと俺に合いにきてやってもいいぜという奇特な方は、メールなり twitter なりで連絡をください(ちなみに早い者勝ちです)。とりあえず一週間ぐらい様子を見て、誰も名乗り出なかったら普通に処分します。
追記:チューナーとかストラップとかギタースタンドは自分で買ってください。
追記2:さっそく貰い手が現れたので締切り。
ANTLR を使う上で気をつけないといけないことの一つは、他のツール(ってか yacc/lex)と違って字句解析と構文解析のコードを同時に生成するということだ。俺はまず字句解析を作ってから構文解析を作るという頭でやっていたので、ちょっとハマった部分がある。
以下のコードは適当に書いた構文解析のサンプルからの抜粋で、いわゆる if 文に相当する部分にマッチしたら、 IF というタイプを設定するコードだ。
class FooLexer extends Lexer;
ACTION :
COMMENT { $skip; }
| "if"! (' '!)+ EXPR (' '!)* ':'! { $setType(IF) } // if $cond: などにマッチ
;
// $foo.bar[1] とかいうのにマッチする。
protected
EXPR : '$' ('a'..'z'|'A'..'Z')('a'..'z'|'A'..'Z'|'0'..'9')*
(
("[" ('0'..'9')+ "]")
| ("." ('a'..'z'|'A'..'Z')('a'..'z'|'A'..'Z'|'0'..'9')*)
) *
;
こいつを ANTLR にかけると FooLexer が作られるが、実際に使ってみるとエラーになる。そしてその理由は「IF が未定義」というもので、 ANTLR では <RULE>: で指定されたルールに対応した名前の定数を自動で定義するが、それ以外のものは antlr パッケージからインポートされるもの以外は一切定義しない(まあ当たり前か)。
ところがこれを次のように修正すると、これはこれで上手くいかない。
class FooLexer extends Lexer;
ACTION :
COMMENT { $skip; }
| IF { $setType(IF) } // if $cond: などにマッチ
;
// $foo.bar[1] とかいうのにマッチする。
protected
EXPR : '$' ('a'..'z'|'A'..'Z')('a'..'z'|'A'..'Z'|'0'..'9')*
(
("[" ('0'..'9')+ "]")
| ("." ('a'..'z'|'A'..'Z')('a'..'z'|'A'..'Z'|'0'..'9')*)
) *
;
IF : "if"! (' '!)+ EXPR (' '!)* ':'!
こうしてしまうと、 if $cond: にマッチしたときの getText メソッドの戻り値が "if $cond:" と文全体になってしまう。実際には IF タイプで $cond という値のトークンが必要なのであり、むしろ if 文全体が生の文字列のまま取れてきてしまうのは都合が悪い。
というわけで、以下のように構文解析部分で IF というトークンの宣言を行うことでこの問題を回避した。
class FooParser extends Parser;
tokenDefinitions:
IF
;
こうすれば、最初のコードを動かすと「IF タイプで $cond という値のトークン」が取れてくる。
まだ ANTLR に不慣れなので実はちゃんとしたやり方があるのかもしれないけど、とりあえず今はこの方法で凌ぐことにする。
『第三回「層・圏・トポス 現代的集合象を求めて」勉強会』に行ってきた。今回の範囲は limit, colimit と米田のレンマを途中まで(定理6の途中で終わった)。何か前回に比べるとえらいハイペースだったんだけど、参加者が圏に慣れてきたのか、それとも何かキメていたのかは定かではないが、とにかく想像以上に進んだのは確か。あと毎回毎回7時間近く行われる勉強会だってのに、何故か新規参加者が増えるという意味不明な現象が起きている。
米田のレンマはまだ途中で終わっているし俺も全然まだわかってないんだけど、とある対象 C で表現される関手から別の関手への自然変換と、その自然変換される先の関手を C に適用した値に対応関係があるって事が言いたいらしいんだけど、それが何を意味するかまではまるでわからず。もしかしたらここからどんどん凄い話題に発展するのかもしれないけど、俺の実力ではまったくわからんのだよ。
あと "Categories for the Working Mathematician" に「圏論の基礎」という邦題を付けた奴は地獄に落ちる権利があると思う。これは「ガチ数学者のための圏論」というのが正しいだろ。ある程度以上数学に打ち込んでいる人が読めば、もしかしたら凄く読みやすい本なのかもしれないが……。
俺は今まで Python でテストを書くときは自前の簡易テスティングユーティリティを使っていたが、仕事ともなれば標準的なツールを使った方がよかろうと言うことで、 unittest モジュールを使い始めたが、久しぶりに使っただけあって変なミスをやらかした。
オフィシャルで紹介されている基礎的な例は当たり前だがきちんと動くんだが、次のようなテストを行うと "TypeError: 'str' object is not callable" というエラーがテストに全部パスしていても出力される。
from unittest import *
class SomeTest(TestCase):
...
if __name__ == '__main__':
main() #これは unittest からインポートされた関数
一体これはどういう事なんだろうと思って首を捻ったが、そういや unittest の main 関数は unittest.TestProgram へのエイリアスで、こいつを引数なしで起動すると __main__ モジュールに読み込まれているシンボルを片っ端から走査してテストケースを見つけるんだった。つまり、 from unittest import * で読み込んだシンボルまでその対象となるので、余計なものが呼び出されるんだった。
from foo import * はなるべくやめた方がいいと言われているが、いや実際その通りだ。でも特にテストケースの場合、大量にインポートしないといけないものがあると使っちまうんだよなあ。
これはもしかしたらあんまりいい習慣じゃないのかもしれないけど、単体テストを作るときに次のような感じでテスト名を付けてる。
class FooTestCase(unittest.TestCase):
def test00NormalCase(self):
u"""正常系のテスト
"""
...
def test01ErrorCase(self):
u"""異常系のテスト
"""
...
いや、実際のテストケース名とその pydoc はもっとちゃんと書いてるけどな。ここで見てほしいのはテストケースに連番を振っているところ。こんなことをしてる理由は unittest モジュールがテストケースを定義された順番じゃなくてテストケース名の辞書順で実行しているからで、ソースコード上での定義順でテストを実行させるには、こうやって連番振るしか思いつかなかったり。
でもこれ、ちょっとバッドノウハウくせえなあ。
追記:なんでソースコード上での定義順にテストを実行したいかというと、なんとなくそんな気分になったからとしか言いようがねえな。
主に家族や友人への私信:俺への連絡に携帯電話が使えると思んじゃない。丸一日メールチェックしないとか、着信に三連チャンで気付かないとか、そういうのがデフォルトだとわかってんだろ。特に集中して仕事というかプログラミングしてるときは、携帯電話への着信とか普通にガン無視放置。
俺へのもっとも確実な連絡方法は gmail のアカウントへメールを出しておくことで、こっちは仕事でも使うアドレスなので一日に何回かチェックしてる。俺から follow されてるなら twitter がそこそこ確実かもしれないが、逆を言えば俺から follow されてない人にとってはよほどのこと(この日記で「twitter でも連絡受付」と表明した時とか)がない限り博打でしかない。そして携帯電話での連絡は、自宅にアポなしでいきなり訪問するのと大して変わらない成功率(見方を変えれば、自宅にいきなり突撃してもそれなりに大丈夫ということなんだが)。
じゃあお前は何のために携帯電話持ってんだと言われそうだが、えーと、その、ほら家具を買って宅配便で送ってもらう時とか、ライフラインまわりの開通作業とか、そういうのの連絡受けるのに必要じゃん。引っ越す度に固定電話を引き直すよりも、同じ携帯電話を使ってた方がまだ楽だと思うし。
携帯電話の料金引き落とし口座の変更手続きでヘマをやらかして、ガチで電話を止められたことがあります。俺にとって携帯電話ってその程度の価値しかないです。そういや今にして思えば、あれは RADIUS で止められたんだろうなー。
ギャワー! というわけで新装版の神聖モテモテ王国を四巻まとめて買ってしまったんだけど、改めて読み返すと本当にとんでもない漫画だ。とにかく言語センスがブッ飛んでいて、どのぐらいブッ飛んでるかというと、この言語センスで真っ当な社会生活を送れるのかどうか不安になるぐらい。
そういえば中学生の頃、別のクラスの友人とお互いに自信を持って勧められるギャグ漫画を貸し合い、それぞれ授業中にその漫画を読み、噴いたら負けという意味不明な勝負をやったことがある。その時俺が貸したのが浦安鉄筋家族で、貸されたのがモテモテ王国だった。
え、勝負の結果? 聞くなよ。
Ardour というソフトウェアをインストールしてみた。いわゆる DAW なんだが、こいつをインストールするのは一苦労だった。いや、 Gentoo にパッケージがあるから emerge 一発だったんだけど、よりによって boost とかの重量級パッケージに依存してたんで、昨日の夜 23:00 ぐらいに emerge したらすべてのプロセスが終わったのは明け方だったようだ(ファイルのタイムスタンプが今日の 04:02 だった)。
一応 MTR も持ってるけど、物は試しということで次はこのソフトで一通りの作業をやってみようかな。もうだいぶ長い間演奏をアップしてないしね。
諸事情によりバイトコードインタープリタを書いているのだが、まさか仕事でこんな事ができるようになるとは思わなかった。まあ、 Python で書いているので C で書くよりは一兆倍楽だ。
ところでプログラミング言語処理系に興味があるなら、「コンパイラとバーチャルマシン」が割とお勧めかもしれない。ってかこれとドラゴンブックぐらいしかプログラミング言語処理系の本は持ってなくて、ドラゴンブックはきちんと読み通してないんだけど。
「コンパイラとバーチャルマシン」は構文解析、中間表現、コード生成、最適化などについて概要をざっと説明しつつ、ターゲットとして JVM を想定したミニ言語のコンパイラを作るという代物で、最初のとっかかりの本としてはいいんじゃないかなと思う。あとはそれぞれの部分について、他の本を読んだりコードを読み書きしていけばいいんじゃないのかなー、というか俺ももっと突っ込んだところについて学ばにゃならんと思うのだが。
数日前に「再帰を使ったプログラムはわかりにくいので再帰を使わせません」とか公言してる恥知らずがごく一部でちょっとだけ話題になったけど、実際のところ再帰はわかりにくいと考えている人って結構いるんだよな。まあ別にわからない事それ自体は本人の習熟度とかそれまでのプログラミング経験の問題なんで、その事自体はそこまで問題にはしないけど、そういうのがチームの上の方に居座って強権振るってるとその下はたまったもんじゃないよな。
ところで俺は最近「入れ子になった辞書(マップ or 連想配列)をマージする」という処理が必要になった。大体次のような感じの処理だ。
x = {
'key1': 1,
'key2': 2,
'key3' : {
'key3-1': 3.1,
},
}
y = {
'key1': 1,
'key3' : {
'key3-2': 3.2,
},
'key4': 4,
}
merge_dict(x, y) # x に無い要素を y からマージ
# 以下の結果が True
x == {
'key1': 1,
'key2': 2,
'key3' : {
'key3-1': 3.1,
'key3-2': 3.2,
},
'key4': 4,
}
ちなみに Python の辞書の update メソッドは使えない。要素を再帰的に見ていかないので、上の例だと key3 の値が {'key3-2': 3.2} になってしまう。なので専用の処理が必要になったが、この処理を行う merge_dict 関数は再帰を使えば超簡単というか、再帰を使わないと余計な変数の再代入が必要になってそれは全然スマートじゃないよな。
def merge_dict(a, b):
for k, v in b.iteritems():
if k in a and isinstance(v, dict):
merge_dict(a[k], v)
else:
a[k] = v
こういう再帰的なデータ構造が出て来るときって、処理の方も再帰的に書いた方が楽な事って多々あるんだけどねえ。
久しぶりにゲームの話題。といっても最近出た奴じゃなくて、だいぶ前の奴だけど。最近遊んだのは「FFCC EoT」と「PSZ」で、どっちも Wifi 対応の ARPG だけど、どっちも Wifi はやらずにソロのみ。それでソロで遊ぶ限りにおいては、明らかに PSZ よりも FFCC EoT が面白かった。
両者はもちろん全然違うゲームなんだけど、だから逆に自分がゲームに何を求めているかがわかるな。改めて思うけど、俺はいわゆるジャンプアクションが好きなんだな。だから FFCC のシリーズで好きな種族は二段ジャンプの出来るセルキー族で、前作 RoF でマゾ性能だったのが EoT ではかなり強化されていてこれはとても嬉しかったなあ。確かに 3D だと距離感が上手く掴めなくて操作性が悪くなりがちなんだけど、それを差し引いてもジャンプアクションのあるゲームの方が俺は好きだな。
あと改めて俺はギミックのない 3D アクションにはそんなに思い入れがないんだなとも思った。やっぱ俺の 3D アクションの評価軸の中核ってロックマン DASH なわけで、ああいう自由奔放にステージ内を飛び回って正解のルートを探したり仕掛けを解くってのにもっとも面白さを感じてしまう。これが 2D になると魂斗羅みたいにひたすら撃って避けてのゲームも好きになるんだけど。
いきなり話変わるけど、最近の DS のラインナップは俺のストライクゾーンから外れすぎだ。まあ、こんだけゲームの裾野が広がった時代において、俺のストライクゾーンは狭すぎるっちゃそうだ。自分で作ろうにも、絵とか音楽の問題があるからなあ。確かに俺は大学の時は一人でプログラムもドット絵も 3D モデリングもやってたけど、グラフィックまわりは「絵が出てるだけマシだろ」レベルだったんで。
生まれて初めて見るオーバーテクノロジー。そうか、世の中には本当に自動でお湯をはってくれる風呂があったのか。というわけで早速使い方がよくわからず悪戦苦闘した。具体的にはお湯をはろうとすると謎のエラーコードが表示されるというものだったのだが、それはガスの元栓を閉めていたせいだった。ちなみに京葉ガスの作業員の人に大まかな使い方を教えてもらっただけで、手元に取扱説明書とかは一切ない。
そういや前のアパートではほぼシャワーしか使ってなかったから、旅行で行った温泉とか抜かすとだいたい三年ぶりだな、湯船に浸かるのは。
Python でそれなりに複雑なモジュール群を作っていると、循環/相互 import の問題にぶち当たることがある。これは読んで字のごとく import 文がループしてしまっている状態の事で、以下のようなファイルを作ることで簡単に再現できる。
#a.py
from b import B
A = 0
#b.py
from a import A
B = 1
ちなみに相互に import しあっても問題のない言語であっても、そのようなモジュールは結合がやたら密なので、基本的にテストしにくく拡張性にも問題がある。つまり真の問題はモジュール間の密結合にあるわけだ。なので相互 import のような問題が起きた場合、何かトリックを使って import 可能にしようなどと思わずに、インターフェースの分離/高階関数/DIなどの技術を使ってモジュール間の結合性を疎にした方がいいだろう。
2.x 系統の Python 版 ANTLR で生成したコードにマルチバイト文字を含んだファイルを食わせると、以下のようなエラーが出ることがある。
File "<path/to/lexer>", line XX, in nextToken
elif la1 and la1 in
u'\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\t\n\u000b\u000c\r\u000e\u000f
\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d
\u001e\u001f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]
^_`abcdefghijklmnopqrstuvwxyz|}~\u007f':
TypeError: 'in <string>' requires string as left operand
これはファイル内のマルチバイト文字が \u0000..\001f の間に収まっていないことに起因する。諸事情により ANTLR では字句・構文解析で需要可能な文字の集合を options 節の charVocabulary で指定しないと、デフォルトで上記のエラーメッセージにある範囲の文字しか受け付けない。まあ、 charVocabulary = '\u0000'..'\uFFFE'; と指定しておけば、まず大丈夫だ。
ちなみに Python の処理系の方から UnicodeWarning が出るようになったりするが、その場合も codes モジュールの getreader 関数でファイルオブジェクトをラップして unicode で文字をを読み取れるようにすれば凌げる。確か Python 3.0 以降ではファイルを開くときにエンコーディングを指定できるので、もしも Python 3.0 以降で ANTLR が使えるならそっちを使った方がいい。
さて俺はここしばらく厳密分離指向テンプレートエンジンの作業をしているのだが、現状では Smarty 風と Genshi 風のテンプレートが一応動く(エラーチェックなどはまだ大甘。プロトタイプ的なステータスだし)という状態には漕ぎ着けている。
このテンプレートエンジンは実行モデルとしてはバイトコードコンパイラ/インタープリタになっており、テンプレート処理をするのに必要そうな最低限の命令で動作する VM を用意し、複数のテンプレート言語をその VM のターゲットであるバイトコードにコンパイルというやり方になっている。まだ仕様を凍結していないのであんまり詳細なことは書きたくないが(既に VM を一回書き直してるし)、今日はちょっと気になることを一つだけ書く。
今の実装では、次のような分岐があったとすると
{if $cond}
<p>foo</p>
{else}
<p>bar</p>
{/if}
以下のようなバイトコードにコンパイルされる。まだ VM を練り上げてないので仕様も実装も泥縄っぽいが、ニュアンスは伝わるだろう。
LOAD cond # コンテキストから cond を読み出し、スタックに push
JMPIF label_else # スタックから pop した値が偽だったら label_else にジャンプ
WRITE "<p>foo</p>" # 出力ストリームに書き出す
JMP lable_end # lable_end にジャンプ
LABEL label_else # ラベルをセット
WRITE "<p>bar</p>"
LABEL lable_end
StringTemplate は基本的なスタンスとしてテンプレート内での演算を認めていないので、条件式は単なる変数参照になる。これでも問題ないと言えばそうだが、じゃあ仮に定数との比較程度なら許すとしたらどうなるだろう。まず考えられるのは次のような文法だろうな。
{if $cond == "xyz"}
<p>foo</p>
{else}
<p>bar</p>
{/if}
こいつをコンパイルした結果は恐らく次のようになる。
LOAD cond # コンテキストから cond を読み出し、スタックに push
PUSH "xyz" # スタックに値を push
EQ # スタックから値を二つ pop し、両者が等しいかテストした結果を push
JMPIF label_else # スタックから pop した値が偽だったら label_else にジャンプ
(略)
実はこれはバイトコードレベルでは対応しているんだが、これに対応するとなると構文解析部分に手を入れないといけなくなる。今のところ if 文も modifier/filter を含んだ変数参照もまったく同一の式言語を用いているのだが、比較などの演算子をサポートすると、 if 文の定義を変えざるを得なくなる。つまり if 文では単純な変数参照と比較演算のみに限定する。これもありといえばそうだ。
そこに手を入れずにやるとなると、 modifier/filter を用いた構文になる。
{if $cond|eq:"xyz"}
<p>foo</p>
{else}
<p>bar</p>
{/if}
Django のテンプレートや Smarty を知ってるなら、 modifier/filter はわかるだろう。まあぶっちゃけ関数呼び出しのようなものだ。それでここでは eq という modifier/filter を用意し、その戻り値で分岐を行うようにしている。バイトコードでは次のようになる。
LOAD cond # コンテキストから cond を読み出し、スタックに push
PUSH "xyz" # スタックに値を push
CALL eq 2 # スタックから二つの値を pop し、それをもって eq を呼び出す。戻り値は再度スタックへ
JMPIF label_else # スタックから pop した値が偽だったら label_else にジャンプ
(略)
「modifier/filter の結果を表示以外に使う」のを気持ち悪いと思わなければ、こっちの方が VM の命令が少なくて済む。
あるいはその折衷案として、いくつかの modifier/filter の構文糖衣を用意するのも考えられる。つまり下記のコードから
{if $cond == "xyz"}
<p>foo</p>
{else}
<p>bar</p>
{/if}
下記のバイトコードを生成する。このとき eq は処理系が組み込みで提供するべき標準の modifier/filter として仕様に明記する。
LOAD cond
PUSH "xyz"
CALL eq 2 # == 演算子を eq の呼び出しに変換
JMPIF label_else
(略)
多分これが一番 VM の命令セットの要素が減り(=実装が単純になり)、なおかつ受け入れられやすい文法になるような気がする。ただしバイトコードの生成は(まあ別に難しくもなんともねえけど)三つの中で一番タルくなるし、何だかんだいってやってることは modifier/filter なので、システムの中の人的にはアレな事にかわりはない。
というわけでいろいろ思案中。
お医者さまから「あんたの頚椎マジでヤバいよ」と言われたでござるよ!
……順を追って書こう。俺は昨日ちょっとゴリゴリとコーディングしすぎたこともあって、今日は自主的に後半休として、午前中にちょっと仕事したら午後から地元に戻り、国民健康保険の加入手続きをする予定だった。「お前一ヶ月近く保険証持ってなかったのかよ」「お前健康面で相当不安抱えてんだろ? 何やってんだ」というツッコミは既に方々から受けているので、今更そういうのはやめてくれ。とにかくそういうわけで保険証を受け取り、そのあとここ数日痛んで仕方のない右手首を診てもらおうとしたわけだ。
俺はてっきり腱鞘炎か何かだろうと思っていたんだが、かかった先の診療所のお医者さまは「……頚椎かなあ」などと不安な事を呟いており、終いにゃ「レントゲン撮ってみるか」などと言い出す始末。俺は内心「勘弁してくれ」と思っていたが、まあとりあえずレントゲンを撮ってみたわけよ。そしたらその結果、俺の頚椎は普通とは逆の反り方をしており、それが負担になって手首の神経に影響が出ている可能性が高いという結果になった。これには流石の俺もビックリした。
しかしなんでまたここ最近になって痛みだしたのかなあと思ったが、運動不足とか不摂生とか心当たりがありすぎる。とりあえず生活のリズムはここ数日で大分安定して来たので、あとはちょっとずつ食生活とか運動とかを改善して行くことにする。でも今の腐った生活習慣って、ぶっちゃけ前の会社に勤めていた頃に身についたんだよなあ。あのまま辞めずに残ってたら、実はもっと深刻な事態になってたのかもしれん。何せ、半分以上人生投げてたからなあ。
第四回「層・圏・トポス 現代的集合像を求めて」勉強会に行ってきた。今回は飛ばしすぎた前回の反動というか反省というか、米田のレンマのあたりをじっくりとやった。が、未だによくわかってない部分があるのであとで書ける気力があったら書く(その前に他の参加者が書きそうだけど)。
追記:hiratara さんが早速書いてくれました。仕事はえー。
「Haskell は極めて強固な型システムなので、コンパイルさえ通ればほとんどデバッグ不要」とかウソだろ。たかが Wiki 文法のパーザ作ってるだけで、デバッガと深い仲になってるぞ。とりあえず今書いてるのコードは以下のリンクより取得可能。
なんでこんなもんを作り始めたかというと、
それで上記のコードから出てくるエラーメッセージは "Main: Text.ParserCombinators.Parsec.Prim.many: combinator 'many' is applied to a parser that accepts an empty string." というものなのだが、一体何が原因でそうなってるのか見当がつかん。しょうがねえからデバッガを起動しているのだが、 Haskell プログラマ Lv1 の俺がなんでデバッガを使ってるんだか。そもそも Haskell でのデバッガはどんな種類があって何がいいのかわかんないから、とりあえず ghci を使ってる。使い方は ghci 上で :? と打てばヘルプが出てくるので、それ見ながらやれば一通りの機能は使えるだろ。
ところで事前にコンパイルしたモジュールを ghci で読み込むと、デバッグ情報が埋め込まれていないのでブレークポイントが設定できないようだ。コンパイルオプションとかまだよくわからないので、そういう時はコンパイル時に生成されるファイルを全部消して、改めて ghci でロードするようにしてる。他にやり方がありそうな気もしないけど、ひとまずはいいや。
追記@30分ぐらい後:デバッガで追って行った結果、バグを特定。 56 行目あたりの以下のコードがバグの原因。
<|> do e <- eof
return $ Text ""
……改めて思う。何やってんだ俺は? なんかすげー意味不明な事やってる。とりあえずこの部分を削除したら動くようになったが、まだまだバグってる(リスト記法が腐ってる)。まあそっちは既にバグの原因を特定してる。
「デコレートのタイミングで、デコレート対象がトップレベルの関数であるかクラスのメソッドであるかを判別する方法はあるだろうか」という問題が出たんで「確か Python では関数とメソッドは types で識別できたよなー」と思ったんで、実際にやってみた(以下、 bpython の画面のコピペ)。
>>> class FOo(object):
... def foo(self):
... pass
>>> def bar():
... pass
>>> import types
>>> isinstance(Foo().foo, types.MethodType)
True
>>> isinstance(bar, types.FunctionType)
True
これをデコレータでやるとこんな感じか。
>>> def dec(f):
... def _(*args, **kwds):
... if isinstance(f, types.MethodType):
... print 'method'
... elif isinstance(f, types.FunctionType):
... print 'function'
... return f(*args, **kwds)
... return _
...
...
>>> class Hoge(object):
... @dec
... def hoge(self):
... print 'hoge'
...
>>> @dec
... def hage():
... print 'hage'
...
>>> Hoge().hoge()
function
hoge
「ええ、何で!?」と一瞬思うけど、よくよく考えれば dec に関数が渡された時点では、それはまだ特定のクラスのインスタンスに属しているわけではないのだ。その辺は dis モジュールで逆アセンブルしつつ、オフィシャルのバイトコード命令を見てればなんとなく掴めるはず。
じゃあどうすんのって話だけど、実は俺は昔に冒頭の課題のようなことをやろうとした事があって、そんときにやったやり方は次のようなものだった。と思う。
実にバカバカしいというか無理やりなやり方だが、一応できなくもない。真面目にやるならもっといろんなパターンを試す必要があるが(例えば STORE_NAME の他にも STORE 系命令あるし)、まあ大雑把にやるとこうだろう。
import dis
import re
import sys
from StringIO import StringIO
class DecoratorInspector(object):
def __init__(self, name, stackframelist):
orig = sys.stdout
self.name = name
self.codes = {}
try:
for s in stackframelist:
sys.stdout = StringIO()
dis.dis(s[0].f_code)
sys.stdout.seek(0)
self.codes[s[3]] = sys.stdout.readlines()
finally:
sys.stdout = orig
def list_decorating_point(self):
for defname, lines in self.codes.iteritems():
for i, line in enumerate(lines):
if '(%s)' % self.name in line and 'LOAD_NAME':
yield defname, i
def list_decorated_function(self, defname, index):
for line in self.codes[defname][index:]:
if 'STORE_NAME' in line:
fname = line.split('(')[1].split(')')[0]
yield defname, fname
def isDefinedInClass(self, defname):
for lines in self.codes.itervalues():
for i, line in enumerate(lines):
if '(%s)' % defname in line and ' STORE_NAME ' in line:
return 'BUILD_CLASS' in lines[i - 1]
def isMethod(self, func):
defmap = {}
for n, i in self.list_decorating_point():
for d, f in self.list_decorated_function(n, i):
defmap[f] = d
n = func.__name__
return self.isDefinedInClass(defmap[n])
使い方はこう。
import inspect
def dec(f):
di = DecoratorInspector('dec', inspect.stack())
if di.isMethod(f):
print '%s is method' % (f.__name__)
else:
print '%s is function' % (f.__name__)
return f
class Foo(object):
@dec
def foo(self):
print 'foo!'
def bar(self):
print 'bar!'
@dec
def buz():
print 'buz!'
このコードを実行すると、次のような出力が得られる。
foo is method
buz is function
かなり穴のありそうな気がするが、俺の知ってる限りじゃこういうやり方しか思いつかなかった。誰かもっとマシなやり方しらないかなあ。