« February 2008 | Main | April 2008 »
Perl でハッシュをマージしたいなと思った。というか Catalyst で config メソッドはどうなっているのか見てみたら、Catalyst::Component の config メソッドで Catalyst::Utils::merge_hashes を呼び出している。merge_hashes($a, $b) としたときに、$a に $b をマージするためにいろいろとごにょごにょしているわけです。そのごにょごにょ(つまり Catalyst::Utils::merge_hashes)を単体で使いたいと思ったけど Catalyst 名前空間のモジュールは依存関係に含めるとちょっと厄介なイメージがあったので、単体で使えるモジュールが CPAN にないか探してみたらありました。
名前からしてズバリな感じです。
使い方は perldoc にあるように次の通り。
use Hash::Merge qw( merge );my %a = ( 'foo' => 1, 'bar' => [ qw( a b e ) ], 'querty' => { 'bob' => 'alice' },);my %b = ( 'foo' => 2, 'bar' => [ qw(c d) ], 'querty' => { 'ted' => 'margeret' },);my %c = %{ merge( \%a, \%b ) };
とすると、デフォルトでは %a から %b に上書きする感じになって結果は次のようになります。
$VAR1 = 'bar';$VAR2 = [ 'a', 'b', 'e', 'c', 'd' ];$VAR3 = 'querty';$VAR4 = { 'bob' => 'alice', 'ted' => 'margeret' };$VAR5 = 'foo';$VAR6 = 1;ARRAYは結合してくれているし、SCALARは上書きしてくれているし、HASHはマージしてくれている。
で merge メソッドの第二引数を第一引数にかぶせたい場合もあるでしょう。そんなときには Hash::Merge::set_behavior メソッドを使います。
先ほどの上記のコードは以下のようになります。
use Hash::Merge qw( merge );my %a = ( 'foo' => 1, 'bar' => [ qw( a b e ) ], 'querty' => { 'bob' => 'alice' },);my %b = ( 'foo' => 2, 'bar' => [ qw(c d) ], 'querty' => { 'ted' => 'margeret' },);Hash::Merge::set_behavior( 'RIGHT_PRECEDENT' );
my %c = %{ merge( \%a, \%b ) };
とすると結果は次の通りです。
$VAR1 = 'bar';$VAR2 = [ 'a', 'b', 'e', 'c', 'd' ];$VAR3 = 'querty';$VAR4 = { 'bob' => 'alice', 'ted' => 'margeret' };$VAR5 = 'foo';$VAR6 = 2;SCALAR である foo が %b の方を採用して 2 になっているのがわかりますね。
merge メソッドの引数の書き方を見れば分かるようにハッシュのリファレンスを渡していますし、merge メソッドの戻り値もハッシュのリファレンスなので次のように書く事もできます。
my $a = { 'foo' => 1,};my $b = { 'foo' => 2,};my $c = merge( $a, $b );そうすれば結果は以下のようにになりますね。
$VAR1 = { 'bar' => [ 'a', 'b', 'e', 'c', 'd' ], 'querty' => { 'bob' => 'alice', 'ted' => 'margeret' }, 'foo' => 2 };これはいい。Hash::Mergeを採用してみることに決めました。
桜が少しずつ花を咲かせていますが、我々のオフィスのみんなと花見に行く計画があります。楽しみです。
多摩動物園に行ってきました。広くて一日ではまわりきれないですね。ライオンバスはおすすめです。ライオンがえさを食べる姿を目の前で見ることができます。これはとても興味深いです。
それから今日はコアラの機嫌がよかったのかわるかったのかコアラが木から降りて床を歩き回っていました。こんな姿はなかなか見られないですね。
いやー楽しかったです。また行こうと思います。
This is a test. please ignore this entry. thanks.
今、XML Schema について少し学んでいるのですが、xs:choice について疑問があって調べました。
xs:choice というのは以下の例の場合、foo, bar, baz のいずれか一つを選択しなければなりません。
<xs:choice> <xs:element name="foo" /> <xs:element name="bar" /> <xs:element name="baz" /></xs:choice>
xs:choice には 2 つの属性があります。
なので、<xs:choice minOccurs="1" maxOccurs="1"> ということになるわけです。でも省略されているから、<xs:choice> は必ず一つを選択するということです。
で、<xs:choice minOccurs="0"> とした場合は 1 つ含まれるかもしれないということになるので、選択してもいいし選択しなくてもよくなります。(具体的にはmaxOccurs="1"なので、0か1ということです。)
<xs:choice minOccurs="0"> <xs:element name="foo" /> <xs:element name="bar" /> <xs:element name="baz" /></xs:choice>
foo, bar, baz のうちの1つあるかないかってことですね。
で、当初悩んでいたのが、次の例です。
<xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="foo" /> <xs:element name="bar" /> <xs:element name="baz" /></xs:choice>
unbounded は無制限の意味になります。ではこのときfoo, bar, baz はいくつ選ぶことができるか?
foo, bar, baz の 3 つでも OK ですし、それこそ foo, foo, bar, bar, bar, baz でも OK らしいです。もう choice の意味がないんじゃないか?と思う程。むしろ、xs:choice をやめて以下のようにしてもいいんじゃないか?と思ったり。(おそらく xs:choice にする意味がどこかにあるからこそ、xs:choice を使っているのだと思いますが。。。)
<xs:element name="foo" minOccurs="0" maxOccurs="unbounded" /> <xs:element name="bar" minOccurs="0" maxOccurs="unbounded" /> <xs:element name="baz" minOccurs="0" maxOccurs="unbounded" />
ま、私は今のところ XML Schema を書くのではなく読む側なので、<xs:choice minOccurs="0" maxOccurs="unbounded"> の意味さえ分かればそれで OK なのですが、それにしても XML Schema は自由すぎて不自由な感じがしました。
ちなみに RelaxNG で書くと以下になります。
( foo | bar | baz )*
ところで、W3C の XML Schema の和訳ってなにげに少ないんですね。。。
参考
テストです。 please ignore this entry.
今日はとても暖かい。こんな日はうたた寝をしてしまいそうです。
今年は花粉が多くて嫌ですね。まったく大変です。
Google Contacts Data API を使った Movable Type のプラグインを実装しようと思っていたのですが、ブログにユーザ増やしたところであんまり意味が見いだせなくてプラグインの実装はちょっとペンディングなんですが、でもせっかく Contacts Data API を使ったのでサンプルをのせておきます。
今回のポイントは Simon Wistow の Net::Google::AuthSub です。GData API 系で必ず使う認証の ClientLogin をラップしてくれます。ClientLogin が成功すると Google から戻って来た値をほんの少しごにょっとしなきゃならないのですが、それを隠蔽してくれるので少しきれいなコードになりますね。
このコードを実行するとメールアドレスのリストが取れます。
#!/usr/local/bin/perl
use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Request::Common;
use Net::Google::AuthSub;
use XML::LibXML;
use Data::Dumper;
my $user = 'YOUR_EMAIL_ADDRESS';
my $password = 'YOUR_PASSWORD';
my $authsub = Net::Google::AuthSub->new(service => 'cp');
my $auth_res = $authsub->login($user, $password);
unless ($auth_res->is_success) {
die 'google login is failed.'
}
my $url = "http://www.google.com/m8/feeds/contacts/$user/base";
my $req = HTTP::Request->new(GET => $url);
$req->header($authsub->auth_params);
my $res = LWP::UserAgent->new->request($req);
my $xp = XML::LibXML->new;
my $xml = $xp->parse_string($res->content);
my @emails = $xml->findnodes("/*[local-name()='feed']/*[local-name()='entry']/*[local-name()='email']");
for my $mail (@emails) {
print $mail->findvalue('@address') . "\n";
}