割とどーでもいい Tips かも知れないけど、いつか自分がど忘れしたときのために書いておく。
xterm, urxvt などの端末エミュレータは -e に続いて起動したいプログラムを書ける。なので、例えば fluxbox で ~/.fluxbox/menu に
[exec] (ssh) {urxvt -e ssh -i ~/.ssh/KEY_NAME REMOTE_HOST_NAME}
[exec] (mail) {urxvt -e shaling}
[exec] (alsamixer) {xterm -e alsamixer}
とか書いておくと、メニューから一発でそれらのアプリが立ち上がるので便利。
理想のフォントのお話、あるいは FontForge でオリジナルフォントをでっち上げるまで。
俺は基本的にフォントは Bitstream Vera Sans Mono か M+ と IPA の合成フォントを使っていた。コードを書く分には Bitstream が優れていて、日本語の表示にはやはり M+ の方が良い。本音を言えば半角英数字記号は Bitstream で日本語が M+ だといいんだが、残念ながら M+ と IPA の合成フォントのうち Bitstream を使っている奴は半角英数字の幅が狭められていてしまって若干視認性が悪い。つまり、ちょうどいいと俺が思っているものが無い。
そこで俺は Bitstream と M+ と IPA の合成フォントをさらに合成して新しいフォントを作ろうと思ったわけよ。フォント作成のためのツールとしては FontForge を選んだ。というか他の奴を知らないだけなんだけど、まあいいか。
この FontForge には独自のスクリプト言語を用いた処理機能があって、文法及び仕様には正直どうかと思うものの、フォントの合成や変換などの処理を行うことが出来る。とりあえず俺が書いたのは次のようなスクリプト。動かす際には、このスクリプトと同じディレクトリに対象のフォントファイルを置いておくこと。
#!/usr/bin/fontforge -script
Open("M+2VM+IPAG-circle.ttf")
SelectAll()
foreach
Scale(200, 200, 0,0)
endloop
Generate("temp.ttf")
Close()
Open("VeraMono.ttf")
SelectAll()
foreach
Scale(110,100, 0, 0)
endloop
MergeFonts("temp.ttf")
SetFontNames("IPAVM-Regular", "IPAVM", "IPAVM Regular", "Regular", "")
Generate("ipavm.ttf")
Close()
最初はもっと単純なコードだったのだけど、その時は M+ と Bitstream を単に合成すると日本語フォントがかなり小さくなってしまうという問題が発生。なのであらかじめ M+ を拡大して保存し、その後 Bitstream と合成している。ところでこの FontForge のスクリプト言語では、どうも Open で開いたフォントがカレントフォント (Perl の $_ みたいな奴) に設定され、それに対して処理を行うらしい。ちなみに俺はこういう仕様が嫌いだ。
これで一応は出来たのだけど、気にくわないことが一つ。三点リーダがダメダメなのだ。恐らく Bitstream Vera Sans Mono には三点リーダが含まれていてそれがダメなのだろうと思って調べようとしたのだが、膨大なフォントから三点リーダを探すのは俺には無理。よってユニコード番号から当たりをつけていくことにした。
$ python
>>> unicode("…")
u'\u2026'
三点リーダのユニコード番号がわかったので、 FontForge を起動して大体多分そこら辺と思われるところを探して、三点リーダを Bitstream より削除。その後上記のスクリプトを走らせて、ようやく目的のフォントを作成できた。
それにしてもフォントって面倒くさいなあ。こんな基本的なフォントの合成だけでも調べないといけないことが結構あったけど、もっと高度なフォント処理とか自分でフォント作成とかしてる人は相当苦労してるんだろうな。俺にゃとても真似できないな。
友人のサイトに突然アクセスできなくなって (403 で弾かれる) どーしたんだろーなーと思って、ちょっとその友人の使ってるレンタルサーバであるロリポップについて調べてみた。そしたらテメー、 Linux 用の英語版 Firefox はアクセス禁止だと!? どうやら UA に英語版かつ Linux と思われる文字列が入っているとアウトらしい。
一応 general.useragent.locale の値を書き換えてどうにかしたけど、まったくなんだよこのクソ会社は。ロリポップ側は SPAM 対策のつもりらしいけど、 User Agent で対策するってのはなんかアドホック過ぎないか? どう考えても抜本的な対策ではないだろ。そういや Wikipedia も同じようなことをやってたな (こっちは Python の urllib の UA とかだった)。まあ Wikipedia の方は単に負荷を軽減したいだけなのでまだわかるが、同じことを SPAM 対策でやるって神経が信じられないな。
Gentoo の portage に python 2.5 が上がってきた。って、大丈夫なんだよな、これ。
で、 emerge しようとしたら python-updater を落としてこれなくてがっかり状態に。Gentoo の Bugs 見たらどうも何か問題が起きて修正してる最中っぽいので、それなのかな。ってか、今日ちょうど問題が報告されてるし。
python-updater が portage に戻ったことだし、 Python を 2.5 にあげてさらに python-updater を動かしたんだけど、どうも上手くインストールされないパッケージがある。どうも vte のインストールでこけていて、その理由が XML::Parser が無いからだと。でも XML::Parser は既にインストールされてるしなあ、と思ってフォーラムとかを探していたら、「XML::Parser を再度 emerge しろ」とのこと。ってことは、どっかでパッケージの情報がおかしくなってたのかな。なんか嫌だな。
まあそういうわけで Python 2.5 での生活が始まったわけだけど、問題が一つ。それはサーバマシンの Python のバージョンで、今はまだサーバマシンの方は Python 2.4。今のところサーバで Python 2.5 の機能を使ったようなコードを使う予定は無いのだけど、なるべくサーバ側もバージョンを合わせておきたい。
これはあれかなあ、サーバマシンも Gentoo にしろってことなのかなあ。別にサーバマシンも Gentoo にしたっていいんだけど、データとかの移行が面倒っちゃ面倒。でも OpenBSD に比べると明らかに Gentoo の方が性に合ってるんだよな。
今週の土日は社員旅行があるので作業出来ないんで、来週あたりに検討してみようかな。
やってられるかバカー。
涼しくなったと思ったらまた暑くなってきて、あまりの寝苦しさに寝付いたのが今日は明け方だった。おかげで 3 時間程度しか寝てなかったんで、会社行ったら眠くて仕方が無かった。いやまあ、いつも眠いんだけど。だって仕事がアホ過ぎてやってらんねえしなあ。
実際のところ今の上司は俺に対して申し訳ないと思っているらしく、まさかここまでバカげた開発になるなどとは思わなかったらしい。そりゃそうだろ。俺もこれに関しては予想以上の酷さだった。やっぱプロマネだか誰だかが「V 字型モデル」とか言い出したときに辞表を叩きつければ良かったかなあ。
最近はスケジュールの変更などの要求がどうにか通ってましになったものの、ほんのついこの間までは完全なデスマーチだったからなあ。今でも結構な人達がとんでもない量の残業をこなしていて、これでストップがかからないってのはやっぱあっち会社の社風なのかな。長時間働けば働くほど評価されるっていう社風。俺はそんな会社は潰れれば良いと思っているし、そういう社風に付き合うつもりは一切無い。大体、長時間の残業が常態化してるってのは明らかに問題があることの表れなんだから、何かしら対策を講じるのが普通だろうに (その点、うちの会社は長時間残業してると警告メールがくるのでまともな部類ではあると思う。まあ、経済的な理由が大きいんだけど)。
いかん、風呂に入った後ついうっかり布団の中に入ったら、妙な時間に妙な分寝てしまった。生活リズムがまた崩れる。
そんなことより、冷蔵庫の中にマジでなにもねえ。台風がいつまでここらに留まるのかわからんが、場合によっては明日の朝飯がヤバいというか、既に腹が減って喉が乾いて仕方がないのだが、この台風の中コンビにまで行くのか? MAJIDE? いま、救急車のサイレンが聞こえてるんだけどなあ。
コンビに行ってきた。あまりにも風が激しくて傘の制御が効かず、コンビニまでの道のりを考えると傘をささずに全力疾走した方が正解だった。
で、今さっき goo の天気予報見たら明日の朝までヤバい状況が続くっぽい。こりゃ明日はいつ会社に着けるのかわからんな。遅くまで残っていたせいで徹夜 or 漫喫の人たちしか午前中に会社にいないという状況がリアルに思い浮かぶ。ってか、台風が接近してるんだから帰れるうちに帰っとけと思うし、そもそもコアタイム以外の時間で 3 時間オーバーの会議をするな。
Python には標準で pydoc というコマンドラインツールが付いてくるのだけど、これの出力が気に食わない。
なので、現在 HTML 出力の部分をガリガリと改造中。方針としては、
ちょいと弄ってみた結果、案外どうにかなってしまった。とりあえず現時点で出来たものとして hackedpydoc.py を公開。使い方は pydoc とまったく同じ。というか、 pydoc -w 以外の挙動は pydoc そのまんま。
げげ、例の改造 pydoc 、クラスのドキュメント出力部分がバグバグだ。というかまあ、そもそも pydoc 使うと継承元のクラスへのリンクが勝手に張られたりするのでローカルで使うならまだしもオンラインで公開するにはあんまり向いてないな。
Python のオフィシャルみたいな構造のドキュメントが理想的なんだけど、あれってどうやって出力してるんだっけなあ。確か __docformat__ で reST を指定して docutils にかけるんだっけ。俺は docutils はデカすぎるのといくつかの文法があんま好きじゃないんだけど、でもそこまで嫌いってわけでもないしなあ。ちと使ってみるかなあ。
どーでもいいけど、前日に社員旅行の集合場所が変更になるってのはどーゆーことだ? 無計画というか泥縄というか。
荒木先生、やはりあなたは神です。というか、
もうみんな素敵過ぎ。
んじゃまあ、ちょっくら社員旅行に行ってきます。帰ってくるのは明日の夕方。
帰ってきた。二日間で伊勢神宮、明治村、徳川美術館を廻るという日程だったわけだけど、明治村以外はケツが四つに割れるぐらいつまんなかったなあ。明治村は割合見所があったのでよかったけど、残り二つは完全に時間の無駄だったな。
それでえらいこと歩き疲れたんだけど、この状態で明日から仕事? 勘弁してよ。夏休みを前提にスケジュールの再調整がされるって知ってれば、明日もう一日夏休みを入れたんだけどなあ。
ぶっちゃけた話、今俺が関わっているプロジェクトは失敗するべきだと思っている。何故ならこのプロジェクトが無事にカットオーバーに漕ぎ着けてしまった場合、プロパーやユーザ企業の上層部は絶対に問題を認識しないからだ (これは同じく酷い状況だったらしい前のプロジェクトが奇跡的に問題なくカットオーバーされ、そして今回は前回の反省がどう考えても生かされていないことからの推測なんだけど)。
これは別に仕事を手抜きしろとかそういう話ではなく、全力投球した上で無残に失敗する必要がある。言い訳不能で手痛い失敗を経験すれば、否応なしに反省するだろ。というか、いっそここらで滅んでみるのも悪くないんじゃないか? デカいところがどっかしら潰れれば、みんな必死になって分析するだろうしな (そういやここ、プロパーの人も知ってるんだっけ? まあいいや、バレたらバレたで)。そもそも IT 業界自体がぶっ壊れてもいいかもしれない。
scribes で日本語の括弧や引用符が入力できなくなってしまった。前に田辺さんが同じ現象にあっていたけど、俺は環境変数を設定して端末から起動してるんだよな。
まあ検討は大体付いていて、今回俺は gnome 系パッケージは最低限しか入れてなくて、 gnome 関連の処理は殆ど走っていない。多分、キー入力のフックのところで何か問題があるんだろ。もう眠いので今日はここまで。
scribes の問題について少し調べたところ、ちょっと面白い解決方法が見つかった。
scribes は全ての機能をプラグインとして実装していて、括弧の自動挿入もやはりプラグインで実現されている。で、このプラグインがキー入力イベントを scim よりも早く拾ってしまうから問題となるわけだ。だったら scim の日本語モードだったらキー入力イベントを拾わせなければ良いわけだ。そう思って書いたのが次のパッチ。
56a57
> self.__is_imemode = False
493a495,496
> from gtk.gdk import CONTROL_MASK
> from gtk import keysyms
495a499,500
> if event.state & CONTROL_MASK and event.keyval == keysyms.space:
> self.__is_imemode = not self.__is_imemode
500d504
< from gtk import keysyms
516,517c520,522
< self.__insert_closing_pair_character(event.keyval)
< return True
---
> if not self.__is_imemode:
> self.__insert_closing_pair_character(event.keyval)
> return True
これを /usr/share/scribes/plugins/BracketCompletion/Manager.py に適用するか、 .gnome2/scribes/plugins/ 以下に先のディレクトリから PluginBracketCompletion.py と BracketCompletion ディレクトリをコピーしてそこで適用するかすれば良い。
これが一体何をやっているかというと、 scim の入力モード切替キーの入力イベント (デフォルトでは Ctrl + Space) をフックして括弧の自動挿入処理を抑制しているだけ。これはかなり乱暴な処理で scim の「全てのアプリケーションで同一入力メソッドを使用」をオフにしているのが条件などちと酷い。本来なら scim の状態を取得して処理を切り替えるのだろうが、多分これは現在の scim では無理。やるんだったら scim にパッチを当ててどこかしら適当なところ (あるいは D-Bus) に現在の入力メソッドの状態を出力して、それを読み取るって処理を付け加える必要がある。
で、俺はそこまでやる気力と時間が今のところ無いので、とりあえずはこれでよしとしておこう。ってか、普通に Gnome 使ってりゃ起きない問題っぽいからな、これ。
今月の Young Guitar は「あの」ギター変態、 Steve Vai のプレイとインタビューが DVD に収録されてるぞ。これはギター弾かない奴でもギターミュージックのファンなら「買い」だろう。
Angelo 先生は相変わらず頭がおかしいなあ。本当、一体どれほどの修行を積めばこの境地にたどり着けるやら。そして呆れたことに、これはまだ先生のプレイの中では最高峰とまではいかなかったりする。 Young Guitar の DVD でも同じ曲を披露したことがあるんだけど、そんときのアンジェロラッシュの速度は法律で取り締まった方が良いレベルに達してたな。ってか、先生の動画を見る度に Dean のギターが欲しくなってしまうから困る。
で、この曲にチャレンジしてる猛者がいたんだけど、この人かなりのレベルで弾けてるじゃん。スウィープは安定してるし、逆手ポジションまで披露してるし、あの謎のハーモニクスを使ったコードプレイもやってるし、アンジェロラッシュも様になってるし。ついに Angelo 世代が本格的に浮上してくるときか? (Trivium のギタリストは先生に衝撃を受けたとか語っていたし、あながち冗談じゃないかも) そういや今月の Young Guitar に Jennifer Batten (Jeff Beck のツアーメンバーも務める超絶ギタリスト) のインタビューが載っていて、そこに彼女の使っているストリングダンパーは Angelo 先生の発明品だと書いてあったな。
もう何度も書いているけど、現場で使われる言語だからって Java や C# を最初に教えようとするんじゃない。現場で使われる言語なんざ、所詮は時代とともに移り変わっていくものだろう。今は Java や .Net 系言語が脚光を浴びていても、それが永遠に続くわけないだろう。
まあ Java が教育に向いているのならそれでもいいんだけど、全然ダメじゃん。そもそもファイルの読み書きにいくつものクラスが必要になる時点で NG。あと標準出力への出力で System.out.println ってのも、初学者にやる気をなくさせるには十分だろう。実際俺も大学に入りたての頃は (先にサークルで C 言語をやっていたこともあって) 「何様のつもりだこの言語は」と思ってた。他にもオブジェクトの配列のソートをするときに比較関数の書き方がなめていたりとか、いろんなことが面倒過ぎる。俺が学びたいのはプログラミングであって、 Java の知識ではなかったのに (当然、 C/C++ にも同じことを感じるようになったが)。もっとも俺の出身大学みたいに文法をまず教え始めるような教育も問題といえば問題で、なんというかもっと面白いことを教えろと。文法なんざそのうち覚えるだろうからよ。
試しに Scribes のプラグインを書いてみた。各種のプログラミング言語ごとにオートインデントとかの機能を提供するプラグインなんだけど、とりあえず Python 版だけ書いた。ってか、俺の場合あとは C と Makefile だけで事足りそうだなあ。
インストール手順は以下の通り。
で、今のところ出来ることは
ぐらい。適当に書いたのでバグが残っているけど、まあそこは直していくつもり。あとは何がいるかなあ。クラスブラウザはあると便利かもしれないし、 pychecker との連携もあると嬉しいかもしれない。
ちなみにプラグインの書き方はまだオフィシャルなドキュメントがないけど、まあソースを読めば大体わかる。とりあえずサンプル、ってか上記のプラグインのコードの一部。
name = 'Language Mode Plugin'
authors = ['Kuwata Chikara <lawless-882@jeans.ocn.ne.jp>']
version = 0.1
autoload = True
class_name = 'LanguageModePlugin'
short_description = 'Support functions for programming.'
long_description = '''Support functions for programming.'''
class LanguageModePlugin(object):
def __init__(self, editor):
self.__editor = editor
self.__manager = None
def load(self):
from SCRIBES.utils import get_language
from SCRIBES.info import home_plugin_folder
import LanguageMode
from imp import load_source
lang = get_language(self.__editor.uri)
if lang != None:
mode = lang.get_id() + 'Mode'
module = load_source(mode, home_plugin_folder + '/LanguageMode/' + mode + '.py')
cls = getattr(module, mode)
self.__manager = cls(self.__editor)
return
def unload(self):
if self.__manager != None:
self.__manager.destroy()
return
コードの要素を上から見ていくと
とりあえずここまでは必須の要素っぽい。
それで肝心のプラグイン本体なんだけど、これは
のが条件で、あとはお好きなように。ここから先は Scribes というよりも PyGtk, PyGObject, PyGtkSourceView といった巨大パッケージの知識の世界だ。ちなみに Gtk は「カーソルのある行の文字列を取得」などといった処理が一発で出来るメソッドがないなど、非常に使いにくい部分がある。さすがに Scribes の作者も頭にきたのか、SCRIBES.utils, SCRIBES.lines などでユーティリティ関数が多く定義されている。
それにしてもだ、俺はプラグインが嫌いなんじゃなくて vim スクリプトや elisp が嫌いだったんだなあ。
とりあえず昨日作った謎のプラグインのロードマップでも書いてみる。
以下は既知のバグ。
それから Scribes のプラグイン作成のノウハウを少々。
なんかアクセスログを見ていて不審に思ったので調べてみたら、この日記の URI がログの収集対象から外れてた。確か昔にワームのアクセスするような URI はログを採取しないように設定したんだけど、それとは違う理由だったはずなんだよなあ。謎だ (当時の作業メモは旧メインマシンの故障とともに消滅)。
もうすでにかなりどうでもいいことなんだけど、無断リンク禁止について。「無断リンク禁止」を本気大真面目に主張してる奴がいたらそいつは確実に電波あるいはバカなので、電波に対する対処方法に従ってガン無視するのが一番 (バカと電波は基本的に同種。バカは病院に入れる必要が無いだけ)。まさか向こうも裁判沙汰にまでしないだろう (そうしてきたら、ほぼ確実に電波どころかサイコ)。ちなみに無断リンク禁止を容認する奴は多分バカ。電波にまではいかないだろう (無知故の無断リンク禁止もあるけど、ただ無知なだけならそれほど時間をかけずに気がつくだろ)。
それでも無断リンクを禁止したい人は「リンクが嫌ならコレ使え!」みたいな簡単極まりない解決方法があるからそれ使えって話だよな (しかし猫と重金属さんは善人だ。俺だったら確実に生成した Java Script に屈辱的なバナーを表示するコードを入れ込む)。
ちなみに「リンクが嫌ならコレ使え!」には Java Script を OFF にすると効果が出ないという欠点があるのだけど、これは少しの工夫でどうにでもなる。具体的には、埋め込む HTML を次のように改変する。
<noscript>
Java Script を On にしやがれ。
<!--
</noscript>
(本文)
<!-- -->
これで完璧。もっとも、ここまでやる奴がいたら完全に毒電波だと俺は思うぞ。
(追記) あ、全然完璧じゃない。ソース見られたら終わりだ。じゃあどうすっかな。ページの本文は CGI + Ajax で別読み込み、読み込みには Cookie が必要でそれは Java Script で仕込む、これでいいか? 流石に Cookie を自分で書き換えるなんてことはされないだろうし (普通は)。
例のプラグインにさらにバグ発見。単語の補完機能と自動アンインデントがコンフリクトしてる。
どうしようかと思って調べたら、 Editor の get_object("WordCompletionManager") で単語補完機能のインスタンスが取得出来るから、それの状態を調べれば良さそう。
(追記) 単語補完機能にバグあり? どうも単語補完のウィンドウが可視化されても、その状態がマネージャに通知されてないっぽい。もうちょい調べる必要あり。
(さらに追記) 上の奴は俺の勘違いっぽい。
調査した結果、以下の事がわかった。
ということで、 CompletionManager の match-found と no-match-found をフックすることでどうにかなった。そもそも互いに補完関係にある二つのパッケージに同名のクラスがあることが混乱の元だったなあ。
LanguageModePlugin をアップデート。今回の変更点は
以下は既知のバグ。
そういや選択モード時に「選択範囲をコメントアウト/コメント解除」があると便利かもしれない。あと将来追加される (今も __future__ から取得できたっけ?) with ステートメントはどうしようかなあ。今から対応するかなあ。
職業訓練真っ最中の妹一号が「新しい PC が無いと不便」と言い出したので、なんぼか (下手すりゃ全額) 俺が出資する事に。それは別にいいとして、事務職となると結局あの糞 Office を使う羽目になるわけで、さらには訓練校では Vista を使っているとのことで、なし崩し的に Vista マシンを買うことになりそうだ。やってらんねえ。
ところで Vista っていろいろバージョンがあるみたいだけど、どれ買えばいいんだ? どうも "Home Basic" というのは極めつけのクソ (ってか半分詐欺) らしいので、多分 "Home Premium" になるんだろう。 Ultimate とかは多分いらんだろ。
LanguageModePluginをアップデート。今回の変更点は
すげーどーでもいいけど、どうやって俺が公式のドキュメント 0 で Scribes のプラグインを書いたのか、その秘密を教えよう。
適当に当たりつけて grep。
PyGtk の知識以外は特に必要無かったんで、あとはどのシグナルがどこで使われているのか調べるだけだったな。
あんまりにも寝付けないのでちょっと起きてメールチェックをしてみたところ、以下のようなバグレポートが。
(1) 新規ファイルをパネルから開く -> Ctrl - s で .py ファイル名を指定したとき(2) リネーム後ときどき(適当ですみません。動いたときと、動かないときがありました)
の場合にインデントが反映されませんでした
でまあちょっと調べて見たところ、 Changer.py に衝撃的なコードが!
self.__siginal_id_1 = self.__editor.connect('rename-document', self.__renamed_cb)
ちょっと待てェェェェェェェェ、なんだその 'rename-document' はぁぁぁぁぁぁぁ!? 'renamed-document' だろうがぁぁぁぁ! コールバック関数の方は renamed になってるのがどうしようもなさに拍車をかけてる。
つーわけで、要するにファイルの保存とリネームのタイミング問題でエラーになってるだけだ。ざっと調べて見たところ
なので、 'rename-document' をフックしてしまうとタイミング次第ではうまく行かないこともあり、また (1) のケースは多分だけどコマンドラインから起動した場合とパネルから新規作成した場合とでは処理が違っていて、それでうまく行ってなかったんじゃないかなあ。
というわけで、ちょっとばっかし緊急気味のリリース。
ざっとしかテストしてないというか、そもそも俺は普段ステータスバーとパネルを隠して使ってるんでこの辺に罠が潜んでいそう。
とりあえず今後の予定というか、最優先事項。
そーいや作ってるときはそんなこと一つも考えなかったけど、このプラグインってどんだけ需要があるんだ? すげぇニッチな気がする (まあ、俺はニッチな物の方が好きなんだけど)。
なーんか Python の os.getlogin() でエラーが出るなあと思ってたら、リファレンスには "For most purposes, it is more useful to use the environment variable LOGNAME to find out who the user is, or pwd.getpwuid(os.getuid())[0] to get the login name of the currently effective user ID." と書かれてた。
どうにも怪しいので少し調べて見たところ、 os.getlogin() って単に getlogin() へのラッパーなのな。その getlogin() の man を読む限り、やっぱこれは使わない方が良さそうだ。
例のプラグインをフォーラムにぶん投げてきたのだが、いきなりアップするパッケージを間違えるというバカをやらかした。とりあえず新しいパッケージを投げてきたが、もしかして俺の書いた Makefile は dbus-python のバージョンによっては動かないっぽい? とりあえず現在調査中。まあ一番困ってるのは、俺の英語力でどこまでバグレポに対処できるかってことなんだけど (既に正しい英語使ってる自信無し)。
あとどーでもいいけど、どうやら風邪をひいたらしい。メールで「体に気をつけて」と書かれた途端に風邪をひく、我ながら凄まじいまでの引きの悪さだ。俺は一日二日休んでも全然大丈夫なんだけど、俺のサポートなしだとテンパイどころかリーチがかかるのが 2, 3 人いて、実はそっちの面倒を見るのが現時点での最優先タスクなんだよな。実際今日も、あやうくスケジュールをオーバーするところだったし。明日どうすっかな。
今朝は目覚めた瞬間、自分の中の誰かが「お前、家の中で遭難してるぞ」と叫び出すぐらい体調が悪く、昨夜布団に入る直前までは辛うじて残っていた「明日明後日ぐらいもつだろうからちょっとぐらい無理しよう」という社畜根性の残りカスは完膚なきまでに消え失せた。
プロとアマの違いについて。俺の考えるプロとアマの違いは、どれだけ「交渉」に直面するかの違いに過ぎない。
明日は前にも書いた通り、妹一号に PC を買ってやらにゃなんらんのだけど、会社の先輩に「ぶっちゃけ Vista ってアリとナシだとどっちですか」と聞いたところ「ナシ。 Me に次ぐクソ OS」と即答された。どうも評判どおりのクソっぷりらしい。そんなものに俺がポケットマネーを出すことになるとは……。
昨日は 21:00 前に寝てしまってこんな時間に目が覚めてしまった。生活リズムぶっ壊れすぎ。
で、唐突だけどバージョン管理ツールの話。俺は普段 darcs を主に使っているのだけど、不満点が二つほど。それは
の二点。前者はラッパースクリプトを書くという苦し紛れながらも一応の解決方法があるのでまだましだが (darcs 本体を改造する気にはとてもなれない。俺は Haskell はろくに使えない)、後者は如何ともしがたい。というか、大抵のツールにはこの機能がない。俺の知る限りこれが出来るのは Borland の StarTeam だけだが、こいつはいろんな意味でダメだ。
コミットログに各ファイルの修正点を一括で書いてしまうというのは勿論ありなんだが、この場合問題になるのはコミットログに書かれたこととファイルの変更自体が紐付けられないということ。別にこれは神経質になることでもない気がするが、なんとなく気持ち悪い。というか、 darcs は一回一回のコミット単位でしか操作をしないのでこの機能がないのは当たり前か。でも他のツールにはあっても良さそうな機能に思えたりする。
そして前から日記で書いていた俺バージョン管理ツールは例の HDD ぶっ壊れ事件により壊滅しているので、さてどうしようか。また一からやり直すか。
実家の PC をセットアップしてきた。初めて Vista に触ったが、確かにこれは酷い。
確かにパッと見は綺麗になった気もするが、他は壊滅的。多分俺は今後何十時間使おうとも Vista を気に入ることはないだろう。
ここ最近残業を出来るだけしないように努力してたら、見事に先月分の残業時間が 10 時間を切った。当然のことだけど残業代は殆ど出ず、残業の嵐の時期の 80% 程度に手取りが落ちたんだけどまあいいや。
そして今月は残業ゼロを目指していたんだけど、他の面子のサポートで残業というパターンが浮上してきた。というか今日すでに残業してきてしまったので、残業ゼロという目標は脆くも崩れた。
前々から書いている通り俺は結構ヘヴィなアレルギー性鼻炎の持ち主で、特にハウスダストの類が死ぬほど苦手だ。そしてこんな時間に日記を書いているということはつまり、久しぶりにビッグウェーブが来て大変なことになっていることに他ならず、なんつーかその、マジでヤバいんですけど。
職場に持ち込んでちまちま読んでた「The Art of UNIX Programming」を読了。内容的には UNIX で有名なコマンドやツールやらをケーススタディとして用いて、 UNIX 流システム開発とそのアドバンテージを説明するというのがメイン。最初と最後の方の章では UNIX の過去と未来についての文章もちらほらあるけど、そこを除けば別に UNIX 系 OS のユーザじゃなくても通用する話が多く、プログラマ全般にお勧めかもしれない。
流石に古参の UNIX 伝道者だけあって若干俺からは「?」だったりする部分があったり (auto tools とか Emacs への評価とか)、そもそも原著は 2003 年に出ているので今とは情勢が全然違う部分があったりとそこら辺は若干気になったかな。全体からすれば大したこっちゃないけど。
何か昨日はえらいこと眠かったんで 21:30 には寝付いてしまっていた。おかげでこんな時間に目が覚めてしまった。
というわけでちょこちょことコードを書きつつ Python の標準ライブラリの difflib なんぞを読んでいるのだけど、これ明らかに diff コマンドと出力が違うよな (使われてるアルゴリズムが全然違う)。俺は今自前の diff を書いてるんだけど、その理由がそこにある。 Python の difflib が UNIX コマンドの奴と同一アルゴリズムで LCS の関数がありゃ問題ないんだけど、そうじゃないからな。
LCS のアルゴリズムは前に書いた奴があるからいいけど、 diff は最初っから書き直しなのが少し面倒だ。やっぱきちんとバックアップは取っておくべきだった。まあ、前に書いたときよりは効率的でコンパクトなものが書けそうではあるけど。
テキストエディタのマクロ・プラグイン言語について。俺は前から書いてる通り Scribes を最近ではメインに使っているんだけど、これはプラグイン言語として Python を使っている。というか、 Scribes 自体がプラグインの集合になっていて、そして Scribes は Python で書かれているというのが正解か。つまり、作りそれ自体は elisp と Emacs の関係に近い。
これは Python プログラマの俺には極めて都合がよいのだけど、じゃあ他の言語のプログラマにはどうだろう。恐らくだけど、俺が書いたような言語毎のプラグインを書こうという人はあまり出てこないんじゃないか。というのも、自分の母国語でプラグインが書けず、そしてホスト言語がプラグイン言語ではなく汎用言語だから (これは同様に汎用言語をプラグインに使っているアプリケーションにも当てはまる)。
elisp にしろ vim スクリプトにしろ、それが汎用言語ではない (elip はかなり怪しいけど) という理由である意味全てのプログラマに平等だ。恐らくどの言語のプログラマも、プラグインを書くために必要なコストは汎用言語で書く場合に比べると大きくは違わない (プラグイン言語の文法に馴染めるかどうか程度の差はあるだろうけど)。
そう考えると、 Scribes はある意味で損な選択をしてるかもしれない。もしもプラグイン言語が汎用言語か否かがプラグイン開発者の絶対数に影響を及ぼすのならばだけど。
何か自分で書いててよくわかんなくなってきた。またいつか書く。多分。
ふとした気の迷いで Fluxbox のコードとか FLTK のコードをほんの少し読んだんだけど、流石に C++ で書かれているだけあって読むのが大変。やっぱ二度と C++ は使いたくないなあ。だからといって C で何か書きたいかというと……。
そして "Fast Light" と名乗る FLTK であれなら、 GTK とか Qt ってどうなってるんだろうな。 wxWindow は考えたくもない。
やっぱさあ、伝統だの文化だのを理由に腐れた組織を野放しにしちゃいけないわけよ。こんだけ大っぴらに事件が明るみに出て、それがどう考えても氷山の一角なんだから、いい加減相撲協会とか解体したら? 別に組織がどれだけ変質しようとも、相撲という競技それ自体がおかしくならなけりゃいいんじゃねえのか。
あんまりこういうことは書きたくないのだが、どうしても我慢できないので書く。あのなぁ、無断リンク禁止を否定して、それがなんで言論の自由の否定になるんだ!?
例えば俺が何かしらのブログサービスとか Web ページのホスティングサービスをやっていて、そこに無断リンク禁止サイトを作られて、そのページを「あ、こいつはダメだ」とかいって削除したりしたらそれは言論弾圧といわれても仕方がない。だって言論弾圧以外の何者でもないからな、それは。
が、自分のサイトに「無断リンク禁止って言ってる奴はとんでもないバカだ」と書いて、それが何で言論の自由に関係するんだ? そうしたからって別にあんたらの書いた物が消えるわけでもないし、 Google 八分にされるわけでもないし、ましてや無断リンク禁止論者が逮捕される可能性なんて 100% ない。それとも他人の意見に異論を唱えたら言論弾圧なのか? それって、そもそもそれ自体が言論の自由に喧嘩売ってるだろ。
まあこれはアンチ無断リンク禁止教側にも言えることで、無断リンク禁止を言論の自由とか表現の自由を理由に批判するのは、やっぱ同程度にアホなことだよな。別に無断リンク禁止という宣言に、なんら法的拘束力はないのだから。まあ実際に訴えを起こしたとか (そしてそれが通っちゃったとか)、もの凄い社会運動になって無断リンク禁止を権利として認めさせようってことだったら話は全然別なんだけど。
Python でドキュメンテーション文字列を少しだけ自由に書く方法 (あくまでも少しだけね)。
ドキュメンテーション文字列の対象となるのはモジュールやクラスなどの __doc__ 変数で、それらは
に書かなければならないと思っていたが、よくよく考えればそこに書かれた物が __doc__ 変数として扱われるだけなので、例えば次のようなコードは完全に合法だ。
def foo():
...
...
...
foo.__doc__ = """クソ長い説明を延々と
"""
あまりにもドキュメンテーション文字列が長くなりすぎるとか、何かしらの理由があったら使ってみてもいいかもしれない。
ところであんま関係ないけど、 decorator を適用した後の関数を公開する場合、次のようにした方が親切だよな。
def somedecorator(f):
def func(*rgs, **kwds):
...
...
func.__doc__ = f.__doc__
func.__name__ = f.__name__
return func
@somedecorator
def bar():
...
...
こうすれば、 decorator を適用した後にも関数名やドキュメンテーション文字列が保持される。
# あ、関係ない話の方が有用じゃねえか
最近全然漫画の感想とか書いてないことに気がついたので、とりあえず思いつくままに。
Python: Myths about Indentation の翻訳。
Python をよく知らない人達の中には、 Python のインデントルールに対してかなりの先入観と誤った考えを持っている。ここでは、それらのいくつかについて書いてみようと思う。
「ホワイトスペースは Python のコードで重要な意味を持っている」
違う、普通はそうじゃない。文におけるインデントレベルだけが重要なんだ (つまりソースコードの左端のホワイトスペース)。他のどこであっても、ホワイトスペースは重要ではないし他の言語のように好きなように使えるんだ。同じように空行 (単なる空行か、ただのホワイトスペースだけの行) をどこにでも挿入できる。
また、互いに関連したネストされたブロック同士のインデントレベルの関係を除けば、インデントの正確な幅は重要じゃない。
その上、明示的あるいは暗黙の行継続を使った場合、インデントレベルは無視される。例えばリストを複数行にわたって分割して書く事が出来、インデントはまったく重要な意味を持たない。だから、次のようなコードを書く事ができる。
>>> foo = [
... 'some string',
... 'another string',
... 'short string'
... ]
>>> print foo
['some string', 'another string', 'short string']
>>> bar = 'this is ' \
... 'one long string ' \
... 'that is split ' \
... 'across multiple lines'
>>> print bar
this is one long string that is split across multiple lines
「Python は正確なインデントスタイルを強制する」
半分正解。まず、やろうと思えばブロックの内部をすべて一行で書くことが出来、よってインデントについて気にする必要はない。次の三つの if 文はどれも正当な書き方でまったく同じ動きをする (簡単のため、出力は省略)。
>>> if 1 + 1 == 2:
... print "foo"
... print "bar"
... x = 42
>>> if 1 + 1 == 2:
... print "foo"; print "bar"; x = 42
>>> if 1 + 1 == 2: print "foo"; print "bar"; x = 42
もちろん、大抵の場合は最初の例のようにブロックを別々の行に分けて書きたいだろうけど、時には似たような if 文の塊があってそれぞれ一行で書いた方が便利な事もある。
もしもブロックを複数行に分けて書こうとするのなら、うん確かにそうだ、 Python はインデントルールについて従うように強制するね。要は「ブロック内部 (上の例の二つの print 文と代入文) は if 文自身よりもインデントされなければならない」ってことだけど、ぶっちゃけた話、他のやり方でインデントしたい? 僕はそう思わない。
だから結論はこうだ: 「Python は構造が不明瞭なコードを書きたいと思わない限り、今まで使ってきたインデントのやり方を使うように強制する」。あるいは「Python は馬鹿げたインデントによる不明瞭なプログラムの構造を許さない」。僕が思うに、それはとてもいいことだ。
こんな C/C++ のコードを見たことがあるかい?
/* Warning: bogus C code! */
if (some condition)
if (another condition)
do_something(fancy);
else
this_sucks(badluck);
ブレース (訳注: {} のこと) を使わない限り else は常にもっとも近い if に対して使われる。よって、このコードはインデントが間違ってるかバグってるかのどちらかだ。これは C/C++ の根本的な問題の一つだ。もちろん常にブレースを使うようにすれば何も問題はないけど、それは退屈な上にソースコードを肥大化させてしまうし、間違ったインデントによって偶発的にコードを不明瞭にしてしまうことを防げない。
Python ではインデントレベルと論理的なブロックは常に一致するので、そんな問題は起こりようがない。プログラムはインデントから期待される通りの動きを常にするんだ。
有名な著作家の Bruce Eckel 曰く:
Python においてはブロックがインデントによって表現されるので、 Python プログラムのインデントは一様になっている。そしてインデントは読み手からすると有意義なことだ。一貫したコードの書式により、私は他の誰かの書いたコードを読むことができ、そして「ああ、わかった。あの野郎、ここからここまでをブレースで括っていやがるな」なんてことでつまづき続けたりすることはなく、そんなことを考える必要はない。
「Python ではタブと空白を安全に混在させられない」
その通りだけど、そんなことしたくないだろう。正確には、 C なんかでもタブとスペースを安全に混在させられない。もしも C のソースコードをタブ幅の設定が違うエディタに移動した場合、それはきっと間違ってみえる (そして見た通りとは違った動作になる可能性がある)。
よって、インデントにタブと空白を混ぜないのは一般的によいことだ。もしもタブと空白のどちらかだけを使うのなら、それでいい。
さらに言えば、タブを使うこと自体をやめるのもいいことだろう。というのも、コンピュータの世界におけるタブのセマンティクスはしっかりと定義されておらず、異なるシステムやエディタではまったく異なって表示されることがある。さらに、タブはしばしばコピー & ペーストで間違って変換されたり破壊されたりする。
殆どのよいエディタはタブの透過的な変換とオートインデント/アンインデント機能をサポートしてる。タブキーを押せばエディタは次のインデント位置にいくのに必要なだけの空白 (タブ文字じゃないよ!) を (8 つかそれとも 4 つかあるいは望むだけ) 挿入し、そして別の何か (大抵バックスペース) で前のインデントレベルに戻ることができる。
言い換えると、それらのエディタはタブキーに求められる振る舞いをしつつも、空白だけを使うことでポータビリティを保っている。これは便利だし安全だ。
もしも自分が何をやってりうのかわかってるのなら、もちろんタブと空白を好きなように使っていし、他の人にソースコードをわたす前に "expand" みたいなツール (例えばこれは UNIX の場合) を使えばいい。もしもタブ文字を使うのなら、 Python はタブを空白 8 文字だとみなして動く。
「ただ単に気にくわない」
OK、嫌いになるのは勝手だし、多分そういう人は他にもいる。仮にインデントでブロックを表現する事が一般的ではなく慣れが必要だとみなされていたとしても、とても多くの利点があるんだ。もしも Python をマジにやり始めれば、本当にすぐに慣れてしまうさ。
ブロック終了の表現に endif みたいな (インデントでない) キーワードを使うことはできる。そういうのは Python のキーワードではないけれど、 Python には "end" を使ったコードを正しいインデントに直して end を取り去るツールがついてくる (訳注: 多分 http://svn.python.org/projects/python/trunk/Tools/scripts/pindent.py のこと。 Gentoo で Python を emerge しても付いてこなかったぞ)。これは Python コンパイラのプリプロセッサとして使うことができる。もちろん、本物の Python プログラマは誰も使ってないけどね。
「コンパイラはどうやってインデントレベルを解析してるの?」
解析の方法はちきんと定義されているし、それはとてもシンプルだ。基本的に、インデントレベルの変更はトークンストリームにトークンとして挿入される。
Python の字句解析器 (トークナイザ) はインデントレベルの保持にスタックを使っている。処理の開始時にはスタックには左端を表す 0 という値が入っている。ネストされたブロックが始まった場合、新しいインデントレベルがスタックにプッシュされ、そして "INDENT" トークンが構文解析器に渡されるトークンストリームに挿入される。一行当たりに存在できる "INDENT" トークンは一つだけだ。
上位のインデントレベルの行に当たった場合、新しいインデントレベルに等しい値が stack のトップにくるまでスタックから値がポップされる (none が見つかった場合、文法エラーになる)。値がポップされるたび、"DEDENT" トークンが作られる。見ての通り、一行あたり複数の "DEDENT" トークンが存在できる。
ソースコードの終わりに達したら、 0 だけがスタックに残るようになるまで "DEDENT" トークンが作られる。
次のサンプルコードを見てくれ:
>>> if foo:
... if bar:
... x = 42
... else:
... print foo
...
次の表の左にあるのが生成されたトークンで、右にあるのがスタックだ。
<if> <foo> <:> [0]
<INDENT> <if> <bar> <:> [0, 4]
<INDENT> <x> <=> <42> [0, 4, 8]
<DEDENT> <DEDENT> <else> <:> [0]
<INDENT> <print> <foo> [0, 2]
<DEDENT> [0]
気を付けてほしいのは、字句解析が終わった後 (構文解析が始まる前) には一切のホワイトスペースがトークンに残されていないということ (もちろん、文字列リテラルは別)。別の言い方をすれば、インデントレベルは字句解析器が扱うものであって構文解析器の扱うものではない。
それにより構文解析器は単に "INDENT" と "DEDENT" をブロックのデリミタとして扱える。これは C コンパイラのブレースの扱いその物だ。
上の例は簡単にしてあって、行の継続などもっとやらなきゃいけないことがある。それらもやはりきちんとした定義があって、興味があるならそれらはすべて (Python の完全で正式な文法を含む) Python Language Reference で読むことができる。
訳者後書き: ちょっと C/C++ の扱いというかブレースを使った言語の扱いが酷い気がするが、実際のところそうなんだから仕方がないか。ちょっと気を抜いてブレースを付け忘れてもコンパイラは怒ってくれないからなあ。ところで Bruce Eckel って「あんたRubyにいいたいことがあるんじゃないんですか? とくにPythonと比べてみて。」の人かな。
どーでもいいけど、物凄く簡単な字句解析のサンプルコードを Python で書いてみた。
def lex(lines):
tokens = []
stack = [0]
for l in map(lambda x: x.replace('\t', ' ') ,lines):
level = count_indent_level(l)
if level > stack[-1]:
tokens.append('<INDENT>')
stack.append(level)
elif level < stack[-1]:
dedent(stack, tokens, level)
token = filter(lambda x: x, l.strip(' ').split(' '))
if token:
print token
tokens.extend(token)
dedent(stack, tokens, 0)
return tokens
def count_indent_level(line):
level = 0
if line.startswith(' '):
for i in line:
if i != ' ':
break
else:
level += 1
return level
def dedent(stack, tokens, level):
while level != stack[-1]:
stack.pop(-1)
if not stack:
raise Exception('syntax error!')
tokens.append('<DEDENT>')
return
次のようなファイルを与えると:
aaa bbb ccc
ddd eee
fff
ggg hhh
次のような結果を返す。
['aaa',
'bbb',
'ccc',
'<INDENT>',
'ddd',
'eee',
'<INDENT>',
'fff',
'<DEDENT>',
'ggg',
'hhh',
'<DEDENT>']
字句解析した結果はブレース使ってるのと変わらないってのがミソだな。
Python の glob の不思議。 glob() 関数を使った結果は大抵アルファベット順とかでソートされているのだけど (既にこの時点で怪しい)、そうなっていないときがある。不審に思ってマニュアルをよくよく読んでみると、 glob モジュールは実のところ os.listdir() と fnmatch.fnmatch() のラッパーみたいなもので、その os.listdir() は
Return a list containing the names of the entries in the directory. The list is in arbitrary order.
なんてことが書いてありやがった。まあそりゃそうか。