Unicode識別子多いなあ


 すんげー昔から思ってたことではあった。何で識別子に日本語使えないんかなー、と。それこそ高級言語を覚え始めた頃から。
 まあ、実装も面倒だろうし、あっても使わないだろうけどな、とは思ったけど。

 で、何かPython3の仕様を見てたら、Unicode識別子に対応してた。ふむ。つーかECMAScriptも対応してた。そうだったのか。
 識別子に使える文字は、UCDのカテゴリー情報で決めるようだ。一瞬重そうに思ったけど、validateは定義時だけだからどうでもいいのか。
 どちらの仕様でも、Unicodeっぽい文字で識別子になるのは、先頭がLu,Ll,Lt,Lm,Lo,Nl、二文字目以降はMn,Mc,Nd,Pcが追加、という感じだった。これらが具体的にどんな文字かは良く知らないが、多分日本語はLoばかりだと思う。Loばかり。熱い響きだな。
 正規化もされるようで、ECMAScriptはNFC、PythonはNKFCを通すようだ。まあ、どうせならそれくらいはしたいよな。日本語だと別にそこまでしなくても全く困らないけど。つーか正規化は定義時以外も掛かるから若干重そうだが。まー富豪だからそのくらいはいいのか。日本語感覚だとNKFCはやりすぎ感もあるが。
 せっかくだから「perl unicode identifier」とか「ruby unicode identifier」とか検索してみた。Perlも対応なのかなあ、よく分からん。正規化はしないのかも。Rubyはもっと分からん。

 はっ。もしかして正規化の掛かる言語の場合、アルファベットの識別子を一箇所間違って全角で書いても通るのか?
 試しにMSのJScriptに簡単なコードを掛けてみた。「エラー: 文字が正しくありません。」とか出た。

 ツマンネー。

 結局、仕様書だけのアレなのかな、JSの場合。まあ、JSであってECMAScriptではない、のか。
 うーむ、どこかで試してみたいなあ。Python3落ちてねーかな。いや、落ちてるけどさ。インストールしないで使える奴無いかな。こんな感じで。これだと2.5だから試せないですが。サンドボックスだからライブラリの制約もあるしな。それは当然だけどな。
 まあ、何だか微妙に面白い時代になっているのだなあ、と改めて思うのであった。いずれ問題点も洗い出され、色々なプログラム言語での実装も進むのかもしれないな。UCDに依存するならば、別に対応すること自体は大した手間でもなさそうな気もするし。

 多分全く使わないスけどね。

 と、ここでオチにするつもりだったが、DSLでは必要かもなあ。エロゲー会社の時に激しく必要に迫られてマジに5分ででっち上げた超糞スクリプトとかも、拡張するうちに変数とか必要になって、変数名は日本語通す仕様にしてたし。NFCとか通せばさらに良かったかもしれん。やってなかったし、特に問題無かったけど。日本語では必要感じないよな。


memoizeの甘い罠に悩む


 さて、memoizeである。メモ化とも言う。最初memorizeと間違えた。
 簡潔に言うと、重たい純粋関数はキャッシュしよう、である。
 平たく言い直すと、同じ引数を入れると必ず同じ物が返ってくるような関数なら、引数と戻り値で連想配列作ってキャッシュした方が速くなることもあるよね、である。まあ、やってる内容は昔ながらのソレである。

 Pythonの場合、モジュール変数は一度しか初期化されないので、実にキャッシュに向いている。しかも今触ってる鯖環境は、しばらくプロセスが生き続ける。もちろん変数も。実においしい。
 というわけで、重くて事前計算も不可能なものはmemoizeしたいのである。

 Pythonにはまた都合のいいことに、デコレータというものがあるのだが、memoizeをデコレータで書くならクロージャを使いたい気がするし、クロージャで縛った変数も鯖応答をまたいで保持されるのかいまいちよく分からんので、素直にモジュール変数を作って、重い関数内にキャッシュを直接実装した。

 テストランはしばらく調子が良かったのだが、どうも具合がおかしく、しばらく悩んだ末にようやく問題点を発見した。
 今回memoizeした関数はリストを返すのだが、Pythonのリストの代入は参照を代入する。戻り値を入れた変数をresultとすると、

cache[tuple(args)] = result

とか

cache[frozenset(kwargs.iteritems())] = result

とかでキャッシュに書き込んだ後、

return result

で終わる訳だが、それを受け取った呼び出し側がそのままリストを加工するとアウトな訳である。何てこったい。高級言語らしい罠だなあ。
 つーかどこもかしこもmemoizeデコレータってこんな感じの実装ばかり見るんスけど。ちくしょう油断した。
 というわけで、

cache[frozenset(kwargs.iteritems())] = list(result)

みたいな感じにコピーを書き込む仕様にした。直らなかった。
 まあ要するに、もう一ヶ所で全く同じ問題が起きていた。キャッシュヒットの時もコピーを返さないとね!
 つーか書き込みもlist()じゃなくtuple()にした方がいいな。それでもshallow copyだから、階層が深いならdeep copyが必要だなあ。

 まとめると、memoizeのキャッシュアクセスは全てimmutableかdeepcopyでなければならない、ってことだな。キーも値も。まあ、Pythonの連想配列のキーはimmutableが元々要求されるから、値だけ注意しろと。
 うーむ、単純ミスだ。参照代入な言語に触ってこなかったからか。むしろ値渡しのコピーコスト削減に必死だったしな…。まあ、参照だと認識してれば当然のバグな訳だが、抽象化の進んだコードにいまいち慣れてないな。C++とかはテンプレートをガシガシ書いてる時でもアセンブラマクロ目線だしな…。

 ところで、壮絶に久々に「りにとか」とかミスタイプして懐かしすぎた。何年ぶりかとか考えたくないものであるなあ。


FirebugとPythonでぐだぐだと


 Firefox使いがWebアプリ書くならFirebugが便利、みたいなのをちょこちょこ見掛けていたが、そこまで劇的に変わるんかなー、とか思って放置してた。デバッガってそんなには使わないし。プロファイラは超大好きだが。VSEEはそこが困る。買えってことだろうけど、開発しやすくした方がWindowsの防衛にもいいんじゃないすかね。

 で、原始的に開発していたが、どうも表示が崩れるバグに遭遇して、原因を探るのがかなり大変そうなので、試しにFirebugを入れてみた。
 控えめに言っても、想像の五倍くらいは上だった。何だこれスゲー。
 リクエスト、レスポンス、それぞれのGET/POSTパラメータ、JSONならデータ構造、DOMやスクリプトやCSSの構造、レスポンスからレンダリング終了までのイベント進行のグラフィカルなタイムライン、クライアントサイドのプロファイラ、と何でもかんでもあった。しかもCSSやHTMLはその場で属性を編集できて、リアルタイムに反映される。
 何つーかまあ、時代なんだろーなー。素晴らしいけど、進化が早すぎてきついな。
 ただし、表示が崩れるバグはDOCTYPEスイッチだったので、Firebugでは見付かりません。

 そんな訳で、サーバ側はPython一本でコーディングもデバッグもプロファイルもほぼ従来通りのスタイル、クライアント側はjQueryにプラグイン載せてコーディングしつつFirebugでデバッグとプロファイル、と。
 まあ、割と楽しいかも。

 つーかPythonも予想より遙かに楽しい。内包表記やイテレータに慣れた辺りから一気に。
 元々はPerlの感覚をベースにしてたから、mapとかgrepとか書きたい時が結構あって、map()やfilter()にlambdaを組み合わせたりしてた訳だが、内包表記で全部片付くのな。まあ、ワンライナー化しないようにはしたいが。
 イテレータも嬉しい。どうしてもリスト処理はオーバーヘッドが気になるし。そういえば変数の代入でも重いコピーが基本的に発生しないから、エイリアスとか関数切り分けとかが地味に楽かも。Javaとかも似た感じなのかね。
 本気出すなら、boost.pythonで簡単にC++モジュールも作れるらしい。本当なら素晴らしいですなあ。試してないから分からんですなあ。
 まあ、そこまで本気出すべきアプリを作る機会は当分無いかもしれんけど。ゲームとか、大きな往来が予想されるサービスとかなら、パフォーマンスは大事だろうけど。そりゃ作りたいっちゃ作りたいけど。


Gmailに走る


 うむ。色々と都合もあったのだ。順に語ろう。

 アンチウィルスソフトを一応入れるようになって久しいが、まあ、役に立ったことは無いし、所詮は気休めである。
 つーか、名前出していいのか分からないから伏せるけど、誰ぞが会社で他人にPC持ってかれて、戻ってきたらウィルス入れられてて憤慨してたのがきっかけだから、要するに自分のミスで踏むことはレアなんだよな。ファイル交換でエロゲー大量落としまくりーとかもしないしなあ。
 それに、どうしたって最新のウィルスはしばらくノーガード素通しタイムがある訳で。

 ということから、有料のウィルス対策ソフトはもういいかな、とずっと思ってはいた。ただ、無料でも何か気休めには入れとこうとは思っていた。
 そして、今使ってる奴の契約が切れたらMicrosoft Security Essentialsでも入れるか、と思っていたのだが、一応今の奴はスパムフィルターも付属してて、1/3くらいのスパムは除去してくれていた。すげー微妙な除去率だが。
 つーか、俺のメールアドレスはWebでも普通に表記しちゃってるし、今更手遅れだろうから全くスパム対策とかせずにそのままな訳だが、おかげでスパムが一日100件くらい貯まる。そうなるとメールボックスを開くのが大変憂鬱になって、メールの返事とかもさっぱりしなくなっているうちに、スパムとECくらいしかメールが来なくなっていた。よって、最近は一ヶ月くらいまとめて受信して、大量のスパムを片付ける作業だけになってた。はっはっは。いけませんなあ。
 んで、スパムフィルターも早い内に探しておかないとなあ、と思っていたのだが、Gmailをスパムフィルターにするのがおいしい、と知った。大まかに調べると、恐らく.forwardでも置いて、あとはGmail側で適切に自動転送する感じだろうか。スパム以外だけ転送、という設定があるようで。いいですね。
 ということで、早速Gmailアカウント作成。ここに今までのメールを受け取る設定を追加して、さらにここから元のメールボックスへの転送設定をすれば、今まで通りにOutlook Expressさんで受信が…。

 Outlook Expressイラネーカラ。

 という訳で、元のボックスに転送せずにGmailをそのまま使うのであった。そういう訳なので、メールくれる方は今まで通りのメールアドレスで問題無いです。あと、ちゃんと読むようになると思います(笑)。
 なお、スパム除去率は素晴らしく、膨大なソレが一通も残らなかった。今までのスパムフィルターは何だったんですか。
 そういえばAkismetのスパム除去も素晴らしいよなあ。最近は本当に一件も残ってないことが普通だし。個人的にはURIBLとか効果高そうだなーと思ってるけどどうなんだろう。


Python3.0仕様のrepr()が欲しかった


 Pythonは3.0で上位互換性を犠牲にして色々とすっきりしたらしい。ちょこっと見ると使いたくなる仕様ばかりだが、まあ、当面は2.5を使う必要があるので仕方ない。2.x系列も既に2.6が出て久しいっぽいけど。

 んで、とにかく読み込みの高速なシリアライズがしたかったのだが、Cライブラリは使えない環境であった。となると、一番高速なデシリアライザは何か。恐らくeval()だろう、素直に考えれば。入力元がAjaxとかだと大問題だが、そういうセキュリティ上の問題も今回は無い。
 であれば、eval()にそのまま渡せる文字列を生成してくれる組み込み関数repr()が最高に嬉しい、…はずなのだが。印刷不能な文字はエスケープされる仕様なんだけど、2.xだと大半が印刷不能という判定であった。つまり、日本語テキストとか全部エスケープされまくり。3.0から改善されたらしいのだが。うぬう。
 といっても、eval()に投げる分には問題は無く、単に人間が読めないだけなのだが、都合により人間も読みたいのだった。うぬーん。
 んで、仕方なくエンコード指定可能なrepr()もどきを自前で書いてみたら、あまりにも簡単に十数分くらいで出来て、動作的にも不満は無く、読み込みも超速かったのだが、どうももやもやするのであった。まあ、新版で解消されてるみたいだから文句を言う筋合いは無いし、古い環境を使わざるを得ない状況になっているのも仕方ない面はあるし…。早く3.0がもうバーンバーン使える世の中になるといいのにね!

 ちなみに自作repr()もどきは、ifを並べたくって、isinstanceとかで場合分けして、集合型はリスト内包の中から再帰させて、最後はelseの中にraise文、みたいなベタな奴だったけど、reprモジュールとやらを使えばさらに格段に簡単そうなことに後で気付いた。

 まあ、とにかく解決したからいいんじゃね。Pythonの練習にもなったし。