D-7 <altijd in beweging>

Day to day life of a Perl/Go/C/C++/whatever hacker. May include anything from tech, food, and family.

2013年02月

Data::STUIDをCPANにあげた。このツールはは前も書いたけど、64ビットの整数IDを作成するための簡単なサーバークライアントツールです。memcachedとそのクライアントみたいなイメージで使えます。

簡単なサーバースクリプトを同梱しておいたので、こんな感じで起動できます:

# バックグラウンドに送るとかは適時ご自分でどうぞ
> stuid-server --host_id=1 --port=9001
> stuid-server --host_id=2 --port=9002
> stuid-server --host_id=3 --port=9003 

で、これらのサーバーをクライアントに渡してやると、適当にIDを取ってきます。 
> stuid-client --server 127.0.0.1:9001 --server 127.0.0.1:9002 --server 127.0.0.1:9003
111499979255802135
サーバーを複数用意しておけば一個が万が一詰まっても、次のヤツに行くようになってます。

もちろん上記のはあくまでちょっと検証するためのスクリプトであって、実際はプログラムの中に埋め込んで使うほうが本来狙っているやり方です :
use Data::STUID::Client;
my $client = Data::STUID::Client->new(
servers => [ qw(127.0.0.1:9001 127.0.0.1:9002 127.0.0.1:9003) ]
);
print $client->fetch_id, "\n";
IDの生成方法自体はSTFのそれと同じで、だいたい順番にならんだ64ビットの整数が返ってくるのでPKとかにいれてそのIDの順番で作成順序でソートしたのと同じ状態にしつつ、ユニークなIDをふれるという感じです。

あとcpanfile対応済みですので、cartonで簡単にサーバーを立てられますよ!

まだ実戦投入とかする段階ではなくて、準備として書いたものなので、なにかツッコミとかあったら是非お願いします。
    このエントリーをはてなブックマークに追加 mixiチェック

2/21 午後、寒気を覚えて初めて確実な異常を自覚した。実は前の晩お酒を飲んだので、その日の朝から若干調子悪かったのもきっと二日酔いのせいだるおと思っていたのだけど、どうもそうではなかったくさい。

午後3時頃ぞくぞくと寒気を覚えて早退。寒くて寒くてしょうがなくて、熱を測ると38.2 Cくらい。この時点ではまだ風邪を疑っていた。夜になって39.2 Cまで発熱。この頃になってようやくおなかがどう考えてもおかしいと気づく。下痢も明らかに進んで、もう水状態のものが出るわ出るわ。

嫁と息子は悪いけど別の部屋に寝てもらって、その日はとにかくまんじりともせずとにかく寝る。夜中に一回「え、俺今吐いちゃうの?」っていうくらいのカジュアルさでトイレで用を足してる間に吐いた。幸い吐いたのはこれ一回のみ。

次の日、朝一番で 病院へ。さすがにこの時点ではノロとかそういう系の物だというのは自分でもわかっている。で、血液検査をしてもらってノロ・インフルの可能性は低いという判断。無事細菌性胃腸炎との診断をいただいた。

薬をもらったものの、基本的にはこの病気は「治った時に治る」系のものらしいので、助けにはなるものの根本的には自分の体に戦ってもうらうしかない。その間もとにかく下痢が治まらなくて辛い。結局木曜に発症して、月曜になるまで一回もまともな排便ができなかった。なにが辛いって、おなかにガスが溜まるのにそれを外にだすために屁ができないんだよ!屁をするとでちゃうんだもん!

ほぼ治った今となっては本当に神様、健康をありがとう、としか言いようがない。

ちなみにその間に月曜に息子が発症・・・がっくり。火曜には嫁が発症?したくさかったけど、軽かったのかもう次の日にはぴんぴんしてた。息子は下痢は治まってきたけど、なにせ自己申告がないからよくわからない。

感染源としてはひとつだけ思い当たりがあって、発症全日の晩に飲みに行った帰りに乗ったタクシーがちょっと酸っぱい匂いがしたんだよねぇ・・・誰か吐いたんじゃないかな・・・

本当、細菌性胃腸炎ってやつぁ感染するし辛いし、息子は下痢をもらしたおむつを床に擦りながら歩き回るし、怖いぜ。みんな気をつけてね!

 
    このエントリーをはてなブックマークに追加 mixiチェック

いつもビッグコミックオリジナルで吉田戦車の「まんが親」 は読んでるんだけど、なんか伊藤理佐のこの漫画のほうがおもしろい気がするなぁ。内容云々よりか子供の描き方というか、タッチな気もするんだけど、特に無表情な時の赤ちゃんの顔は圧倒的に伊藤理佐バージョンのほうがかわいい。

あと、さすがにあんこ缶にアイスクリーム入れて食べるのとか、羊羹を食パンに挟んで食べるのは大人としてアウトな 気がします!(←詳細は読んであげて!)
    このエントリーをはてなブックマークに追加 mixiチェック

日本に住むポルトガル人のマルタが日本でポルトガル料理を作ってみたり、はたまカップ麺を食べてみたりする。ポルトガルとは縁があるので気になっていたのだが、いざ読んでみるとイマイチポルトガル人である必然性が見当たらない・・・。もうすこしポルトガル料理を作ってみたらどうなんだ!特に風邪を引いた話の回とかではcaldo verdeくらい作っていただきたかった・・・
    このエントリーをはてなブックマークに追加 mixiチェック

追記:名前が同じだと微妙かな、と思ったので変えた。

追記2:ついでにCPANにうpした

https://github.com/lestrrat/p5-Kage https://github.com/lestrrat/p5-Geest 書いてみた。それだけ。後悔はしてない。

Twiggyとかで動かす。

plackup -s Twiggy -a app.psgi
# または twiggy -a app.psgi とか

別に元のバージョンでよかったしなんの問題もなかったんだけど、ただ自分で書いてみたかった。
    このエントリーをはてなブックマークに追加 mixiチェック

約1年強ぶりに藤子・F・不二雄 ミュージアムに行ってきた。大長編ドラえもんの企画展やるっていうのと、もう嫁が今月末から職場復帰する予定だからしばらく平日にいくの無理だなーと思って前売り券を買った・・・のはいいものの、数日前から天気予報がずっと雪!雪!雪!

 で、今朝の感じを見たところ降っているところもあったみたいだけど、それほど酷くなさそうなので強行決定。朝イチの入場に間に合うように移動。

前回来た時はまだ子供は産まれていなかったなぁ、などと考えつつ、到着。会場外の展示も微妙に変更されてた。


続きを読む
    このエントリーをはてなブックマークに追加 mixiチェック

近所の酒屋で予約販売してた日本酒。普段日本酒はつぎの日に響くから買わないし飲まないと貫いているのだけど、これはなんか美味そうな予感がしたので購入。千葉の酒蔵で作っているようなのだけど、これはその日の朝瓶詰めした日本酒をその日のうちに買えるというもの。



微量の発泡もあり、すっきりさわやか。あんまり日本酒っぽくないかも。ひじょう〜においしかった。これならもっと買っておけばよかったな。来年も販売する時は酒屋から電話来るって言われたから来年も買おうかと思う
    このエントリーをはてなブックマークに追加 mixiチェック

子供ができて今までとライフスタイルが変わって時折自分に言って聞かせてる事が一つあって、それはなにかというと「子供ってのはギャーギャー泣いたり騒いだり言うことを聞かないもんなんだから少しでも良い子にしてくれてたら良い子だったね、って言ってあげよう」ということ。

もちろん他人に迷惑をかけない、ってのは前提条件なんだけど、まぁちょっと声あげたとか、少しぐずったとか、要は実害までいかないけど親が「わ、やべぇ!」と思うような時って結構あるわけで。もちろん親側でも色々と予防対策はするけど、それでも子供は思い通りに行かないのが前提だって思えると後々大分気分が楽。

今日も夕飯は外食で済ませたんだけど、座敷の席でなおかつ回りに子供がたくさんいるところだったから割と気楽な先で助かったもののやっぱり途中1、2回きゃーとかいったくらいで良い感じで過ごしてくれた。まぁまぁぐずったけどさ。

でもやっぱり子供が騒ぐもんだと思ってれば予防措置もとれるし、それでもやらかしてしまった場合でもまぁまぁ、最悪の事態にはならなくてよかった、と思えるしいいと思う。

うちに帰ってきて「今日もありがとな」って息子に言えるのは幸せな事だな、と思う。

    このエントリーをはてなブックマークに追加 mixiチェック

サイン会は2/3。その約一月前にポテン生活大ファンの嫁が「あ、渋谷TSUTAYAで10巻を買うと先着100名様にサイン会の整理券もらえるんだって!」と言うので、嫁のためにその発売日朝10時開店と同時にTSUTAYAに突入。整理券番号は確実に殆ど最初だろ、だってこの漫画そんなに大勢ファンがいるとは思えないし・・・とか思っていたらなんと7番。開店と同時に行ったのに、俺の前に7人もこの漫画を買った人がいるわけ?侮っていた。

まぁそんなわけで2/3お昼くらいに行った。サイン会スペースがあまりにも狭すぎるのでサインをもらう嫁しか入れなかった・・・くっ・・・木下さん、みたかったよ・・・。というわけで俺と息子は店内で待機。もちろんこのあたりの待ち時間とかも考慮して息子の授乳タイムやらなにやらもすべて調整済み。

30分くらいしてサインをもらって出てきた嫁が見せてくれたのがこれ。



どのキャラクターがいいですか?って聞かれたらしいので「駄菓子やのばばぁと子供達」と答えたそうな。ナイスチョイス。 俺もこれがよかった。嫁によると木下先生はさわやかな方だったそうな。 いいなー、俺も会いたかったなぁ。
    このエントリーをはてなブックマークに追加 mixiチェック

以下のコードはDigest::MD5のオブジェクトAPIの使用方法。
use strict;
use Digest::MD5;

my $md5 = Digest::MD5->new;
$md5->add("foo");
$md5->add("bar");
$md5->add("baz");
warn $md5->hexdigest; # 6df23dc03f9b54cc38a0fc1483df6e21
$md5->add("hoge");
warn $md5->hexdigest; # ea703e7aa1efda0064eaa507d9e8ab7e
warn $md5->hexdigest; # d41d8cd98f00b204e9800998ecf8427e 
最初のhexdigest() の呼び出しは "foo", "bar", "baz"の値を使って計算したMD5の値。次の呼び出しは違う値を作成し、その次もまた値は違う。

これはどういうことになっているかというと、最初のhexdigst()の呼び出しはそれまでにadd()で追加してきたデータを使用してMD5の計算をしているのだが、hexdigest()を呼んだ瞬間に 内部ステートがリセットされて、また一からデータを追加していくことになる。

ということは、最初のhexdigest()は "foobarbaz"から計算したMD5で、その次は"hoge", 最後のは空文字列から計算した値。

Digest::MD5は一旦MD5を計算したら、それまでのステートをそこから先に持ち越さない。

この実装は当然だと思う。なんでかというと このオブジェクトは引数から値を作成するためのオブジェクトであって、何かの状態を表すためのオブジェクトではない。計算をするためのオブジェクトは、その計算を確定するまでステートを保持してもいいけど、その後は持っている必要も意味もないし、それよりか有害だ。$md5->add("hoge")した時にそれまでの"foo", "bar", "baz"から計算するための途中結果とかが残ってて計算結果に影響を与えたら、とか想像するとgkbrじゃない?

これに対して、例えばDBから値をもってきてそこから何か思い計算をする何か・・・例えばあなたの株口座を表すオブジェクトがあって、それを使い回している。時折特定の銘柄の過去一ヶ月の自分の株の損益評価をグラフにしたいからそのシリーズを取得しなければいけないようなオブジェクトならどうか

my $portfolio = Stock::Portfolio->new( user_id => "lestrrat ");

while ( ... ) {
# 時折 NTTの過去一ヶ月の取得株価と任意の日の株価の差額をリストで欲しい
my @profit = $portfolio->get_profits_last_30days( 9432 );

# そのほか $portfolioで色々操作するから $portfolio自体は
# 使い回す。
}
このような計算を必要とするなら過去30日分の株価をオブジェクト内にキャッシュしてるのはわかる。ただしここでも気をつけないといけないのはキャッシュするにしても銘柄コードをキーにしてキャッシュしないといけない。例えばインメモリでキャッシュするにしても以下のように"last_30days"のようなキーでキャッシュしてはいけない

sub get_profits_last_30days {
my ($self, $code) = @_;
...
$self->{last_30days} ||= $self->get_prices_last_30days( $code );
...
}

これだと$codeの値が変わってもlast_30daysの値が変わらない。ひどい罠ですね。以下のように必ずどの銘柄コードのキャッシュなのかわかるように格納しないとまざってしまう危険性が高い。

sub get_profits_last_30days {
my ($self, $code) = @_;
...
$self->{last_30days}->{$code} ||= $self->get_prices_last_30days( $code );
...
}

あと大事なのはキャッシュをするなら、キャッシュがパージされるタイミングがあるのかよく考えること。この例の場合は日付が変わったら当然新しい値が追加されて、一番古い値が消えるだろうから キャッシュパージのタイミングは日付だ。だからキャッシュ内にいつまで有効なデータか書いておくか、memcachedのような「別に消えても大丈夫」なキャッシュだったらキーに日付も含めておけばいい。

上記のようなステート保持が必要ないけど、それでも重い処理ならまずは「変数」として持って、必要な関数に渡しましょう。一番シンプルです!

どうしてもオブジェクトに保存したいならlocalを使うか、ガードオブジェクトを使いましょう。

# 最もシンプル。変数に格納しておく
sub foo {
my $self = shift;
my $data = $self->calculate_some_data; # 重い処理
$self->bar( $data );
$self->baz( $data );
}

# どうしても $selfに入れたい (1) local
sub foo {
my $self = shift;
local $self->{data} = $self->calculate_some_data; # 重い処理
$self->bar; # $self->get_data(); とかでキャッシュされたデータにアクセス
$self->baz;
}

# どうしても$selfに入れたい (2) ガードオブジェクト
use Scope::Guard;
sub foo {
my $self = shift;
my $guard = Scope::Guard->new(sub {
delete $self->{data};
});
$self->{data} = $self->calculate_some_data; # 重い処理
$self->bar;
$self->baz;
}
あともう一つの方法としては関数開始時に格納してあるフィールドを初期化する、とかあるけど、そもそもただのキャッシュで、次に計算する値が同じ物になる確率が低いなら関数を抜けた時にちゃんとクリーンアップされてるほうが筋がいいと思う。

最後に、それでもどうしてもそういうコードになってしまう場合。そういう事もあるかとは思います。現実はセオリー通りにはいかないです。その場合はしょうがないのででっかいコメントブロックを儲けて、なんでそんな事をしないといけなかったか、400文字程度の反省文を該当メソッドの前に書いてください。 そこまですれば後任の人が髪の毛をむしる必要がなくなりますね。

よろしくお願いいたします。
    このエントリーをはてなブックマークに追加 mixiチェック

おおおおお、ひっさしぶりにヒット!この話はよくできてるし、読後感も良い。普段は翻訳ものは嫌いなんだけど、これは翻訳もよかった気がするな。ここのところ割と流し読みが多かったけど、こいつは久しぶりに先が読みたくてうずうずしながら読み続けた。おすすめ。
    このエントリーをはてなブックマークに追加 mixiチェック

誉田さん、やってしまいましたな。「ジウ」を読んだときも「この人女性の書き方がうまくないなー」と思ったけど、今までまぁまだ許せる範囲だったストロベリーナイトシリーズでとうとうやっちまった感。ラストが全く共感できない・・・ラスト・・・かなり納得できない。
    このエントリーをはてなブックマークに追加 mixiチェック

特にまだ用途はないのだけど、STFの64ビットID生成ロジックにNet::Serverでフロントエンドつけて、さらにCache::Memcached風味な、複数サーバーのうちどれからかIDを取ってくる、という形でユニークIDジェネレーター書いてみた

Data::YUIDでよかったんだけど、コード読んだらなんか微妙なプロトコル がハードコードされてるし、ID払い出すだけでいいんだから、ソケット接続→即IDを返す!でいいような気がしたし、それならクライアントもそれだけ簡素化できるしーと思って思い立って書いてみた次第。

サーバー側は適当に固有のhost_idをふってあげて、何個か立ち上げてあげる:
use strict;
use Data::STUID::Server;

# perl server.pl <id> <port>
# perl server.pl 1 9001
# perl server.pl 2 9002
# とか Data::STUID::Server->run( port => $ARGV[1] || 9001, host => '127.0.0.1', host_id => $ARGV[0], );
で、それらにアクセスするようにクライアントを設定する感じ
use strict;
use Data::STUID::Client;

my $client = Data::STUID::Client->new(
    servers => [ qw(
        127.0.0.1:9001
        127.0.0.1:9002
    ) ]
);

for (1..10_000) {
my $id = $client->fetch_id;
print "Got $id\n";
とりあえずざっくり。
    このエントリーをはてなブックマークに追加 mixiチェック

このページのトップヘ