あるソフトウェアエンジニアの戯言

日々の開発やサーバ管理のなかで出てきたあれや、これや、あんなことや、こんなこと

2008年11月の記事一覧 < 2007年3月2009年3月 >
この記事のキーワード:   WebBrowser   chrome   google

ものすごく地味な機能だけど、google chrome でテキスト部分をダブルクリックすると、辞書を参照しているとしか思えないような選択の仕方をする。
Firefoxやその他のWindows/linuxのアプリケーションでは、日本語をダブルクリックして単語選択しようとすると、平仮名、片仮名、漢字の連続を単語として認識するのが通常の動作だけれど、chromeじゃ、辞書に応じた選択がおこなわれる。
例えば、「この記事編集部分では」なんて文字列があるとき、「編集」あたりでダブルクリックすると、Firefoxなどでは「記事編集部分」の6文字が選択されるが、chromeじゃ「編集」だけが選択されるという具合。

動作に遅延もないので、ネットワークにアクセスしながらやっているわけではなさそう。

はたして、どうやってるのか、、、、
わからなければ、ソースがあるんだから、探せばいいやん、ということで、ソースプログラムを当ってみた。
果して、問題の箇所を発見。(linux版 chromiumのソースツリーを探索しました)

src/third_party/icu38/source/data/brkitr/.svn/text-base/cjdict.txt.svn-base

上記ファイルに40万語ほどの日本語辞書を発見しました。この辞書をもとに単語境界を検索している模様。
日本語以外にも簡体中国語とタイ語の辞書があるようなので、少くともこの二つの言語では同様の処理が可能なのではないかと推測される。
やるねぇ、google。

この記事のキーワード:   Moose   perl

Moose::Cookbook::Basics::Recipe3
- ちょっといい加減な二分木の例

概要

         package BinaryTree;
         use Moose;

         has 'node' => (is => 'rw', isa => 'Any');

         has 'parent' => (
             is        => 'rw',
             isa       => 'BinaryTree',
             predicate => 'has_parent',
             weak_ref  => 1,
         );

         has 'left' => (
             is        => 'rw',
             isa       => 'BinaryTree',
             predicate => 'has_left',
             lazy      => 1,
             default   => sub { BinaryTree->new(parent => $_[0]) },
         );

         has 'right' => (
             is        => 'rw',
             isa       => 'BinaryTree',
             predicate => 'has_right',
             lazy      => 1,
             default   => sub { BinaryTree->new(parent => $_[0]) },
         );

         before 'right', 'left' => sub {
             my ($self, $tree) = @_;
             $tree->parent($self) if defined $tree;
         };

例によって、ここまで


とりあえず、サンプルプログラムを読んだだけで、いままでのレシピにはなかった、いくつかのことがわかる。
列挙してみる

  • attributeの 'Any'型指定
  • has の predicate オプションの存在
  • has の weak_ref オプションの存在
  • has の lazy オプションの存在
  • has の default オプションへ 関数リファレンスを渡すということ
  • before の引数として、複数のメソッド名を渡すことが可能だということ

で、説明を読むに、それぞれ、以下のようなことらしい。

attribute の 'Any'型指定
どんな値でもいい。ようするに型の指定なし、ということ。isaオプションが未指定の場合と全く同じ。
has の predicate オプション
そのattributeが存在するかどうかをチェックするメソッドをオプションに指定した名前のメソッドとして生成してくれる
has の weak_ref オプション
そのattributeが weakend referenceだという指定。 weakened reference については「プログラミングperl」なりその他のドキュメントを当るべし。
has の default オプションに関数リファレンスを渡す
defaultに渡す値が文字列または数値以外の(初期値を指すリファレンスになるような)場合は、リファレンスをそのままdefaultに設定してしまうと、すべてのインスタンスで実体が同じものを指してしまって具合が悪いので、関数リファレンス中で生成した個別の実体を指すようにしなくてはいけない。そのために、関数リファレンスをdefaultの値として渡す、ということですね。これで、初期値を得るために、この関数が実行されて、戻り値を初期値として設定する、という動作になる。これは、attributeの型がオブジェクトではなくて、array や hash の場合でも同様。(新しく生成した配列やハッシュのリファレンスを返す関数を渡す)
has の lazy オプション
まず、defaultオプションがないときにはlazyオプションの指定はできない(するとクラス生成時(*)にエラーになる)。このオプションが指定されると、attributeの生成は可能な限り遅らされる。最初に値を格納しようとしたときは、初期値の生成は実行されずに、指定した値を格納する。逆に、値を読み出そうとしたときは、その時点で初期値を生成して、値を返す。
before の引数として、複数のメソッド名を渡せる
これは見たまま。おそらく、最後の引数をそれ以前の引数で指定されるメソッドに適用する、という実装になっているのだと思われる。ちなみに、このbeforeで設定される関数でツリーの枝に接続するノードのparentノードを自分自身に設定するようになっている。
この記事のキーワード:   Moose   perl

Moose::Cookbook::Basics::Recipe2
- 簡単な銀行口座の例

概要

package BankAccount;
use Moose;

has 'balance' => (isa => 'Int', is => 'rw', default => 0);

sub deposit {
  my ($self, $amount) = @_;
  $self->balance($self->balance + $amount);
}

sub withdraw {
  my ($self, $amount) = @_;
  my $current_balance = $self->balance();
  ($current_balance >= $amount)
    || confess "Account overdrawn";
  $self->balance($current_balance - $amount);
}

package CheckingAccount;
use Moose;

extends 'BankAccount';

has 'overdraft_account' => (isa => 'BankAccount', is => 'rw');

before 'withdraw' => sub {
  my ($self, $amount) = @_;
  my $overdraft_amount = $amount - $self->balance();
  if ($self->overdraft_account && $overdraft_amount > 0) {
    $self->overdraft_account->withdraw($overdraft_amount);
    $self->deposit($overdraft_amount);
  }
};

例によって、転写はここまで。


気になったところ

この例で気になったのは、extends でも before でもなくて、BankAccount の withdraw メソッドの中にある confess という関数。
文脈から warn とか die とか croak みたいなもんだろうな、とは想像がつくのだけれど、実際に実行してみないことには、、、ということで、実行してみる。
withdraw に balance 以上の値をわたして実行してみると、以下のような出力が返ってきた。

Account overdrawn at BankAccount.pm line 14
BankAccount::withdraw('BankAccount=HASH(0x9c9ef50)', 101) called at BankAccount.pm line 30
Class::MOP::Class:::before('CheckingAccount=HASH(0x982a6e8)', 151) called at /usr/lib/perl5/Class/MOP/Method/Wrapped.pm line 47
Class::MOP::Method::Wrapped::__ANON__('CheckingAccount=HASH(0x982a6e8)', 151) called at /usr/lib/perl5/Class/MOP/Method/Wrapped.pm line 89
CheckingAccount::withdraw('CheckingAccount=HASH(0x982a6e8)', 151) called at moose.pl line 13

ようするに、バックトレース付きの croak ってことですね。おそらく Moose.pm からexport されている関数だろうし、APIリファレンスを当たれば説明がでているのだろうけど、今回は放置。。。。
FOOTNOTES の (4) を読むと、''throw an error'' とあるので、やはり croak と同様に die を実行するみたい。

あと、上の概要に示されているソースプログラムをmainプログラムの末尾にくっつけて実行してみたら、

Can't locate object method "balance" via package "BankAccount" at moose.pl line 27.

といわれてエラーになってしまった。(''moose.pl line 27'' はちょうど ''$self->balance($self->balance + $amount'' の行)
BankAccount.pm って別ファイルに保存して use BankAccount; すれば正常に動作したので、なにかがマズかったんだろうけれど、なにがマズかったのかまでは未調査。
FOOTNOTES の (2) で クラスが使用される前にロードされていなければならない、みたいなことが書いてあるので、コンパイルされる順序の問題のようだ。
ようするに、該当するクラスを new する前に use するなり、定義してやるなりしないといけないということのよう。

その他のハイライト

  • has のオプションで isa に 'Int', default に 0 を設定している (BankAccount の ''balance'' attribute)。コンストラクタに引数として渡されない限り、この初期値で初期化される。
  • has のオプションで isa の値にクラス名が使える。(CheckingAccount の ''overdraft_account'')
  • スーパークラスのメソッドの実行前に実行される ''before'' method modifier。 SUPER::method_name(args) を使ってもほぼ同じことが実現可能だが、スーパークラス側の引数が追加された場合などに追従するのが大変になる。
  • isa=>'BankAccount' と指定されているときに'CheckingAccount'はBankAccountのサブクラスなので、代入可能である。(通常のオブジェクト指向と同じ)
この記事のキーワード:   Moose   perl

Moose::Cookbook::Basics::Recipe1 - (古典的な)Point型の例

概要

   package Point;
   use Moose;
  
   has 'x' => (isa => 'Int', is => 'rw', required => 1);
   has 'y' => (isa => 'Int', is => 'rw', required => 1);

   sub clear {
       my $self = shift;
       $self->x(0);
       $self->y(0);
   }

   package Point3D;
   use Moose;

   extends 'Point';

   has 'z' => (isa => 'Int', is => 'rw', required => 1);

   after 'clear' => sub {
       my $self = shift;
       $self->z(0);
   };

   ....

   # hash or hashrefs are ok for the constructor
   my $point1 = Point->new(x => 5, y => 7);
   my $point2 = Point->new({x => 5, y => 7});

   my $point3d = Point3D->new(x => 5, y => 42, z => -5);

解説

古典的な Point の例である。Perl 6 Apocalypse 12 や K&R C にも似たような例が載っている。

Perl 5 の他のクラスと同様、Mooseのクラスもパッケージとして定義する。 Mooseは "use Moose"を宣言した時点で "strict" 及び "warnings" を自動的に有効にする。

パッケージにMooseが読込まれると、'sugar'関数がエクスポートされる。これらは、Mooseのキーワードとして機能する。これらはperlのキーワードではないが、パッケージにエクスポートされた時点でそのように振る舞うように設計されている。

Moose は作成されたパッケージを自動的に Moose::Object のサブクラスとして定義するようになっている。 Moose::Objectクラスは属性(attribute)やその他の機能を果たすようなコンストラクタを提供する。詳しくは Moose::Object を参照のこと。


と、ここまで書いたけど、全部訳していると、時間がかかってしょうがないので、これ以降は、気がついたことや、やってみたことなどだけ、、、


method modifier

メソッドをサブクラスで変更したい場合は、 method modifierを使う。
例には after キーワードで

after 'clear' => sub {
  my $self = shift;
  $self->z(0);
};

なる例が出てくる。
これを、 override キーワードを使って

override 'clear' => sub {
  my $self = shift;
  super();
  $self->z(0);
};

とやっても、全く同じになる。
ここで、疑問発生。
引数がsuper()関数にも全く同じものが渡っているのだが、perlの関数引数(@_)ってば、呼び出された側で書換え可能だったはず。
どうなってるのかと思って、

use Moose;

extends 'Point';

has 'z' => (isa => 'Int', is => 'rw', required => 1);

override 'clear' => sub {
  my $self = shift;
  $_[0] = 10;
  super();
  $self->z(0);
};

# hash or hashrefs are ok for the constructor
my $point1 = Point->new(x => 5, y => 7);
my $point2 = Point->new({x => 5, y => 7});

my $point3d = Point3D->new(x => 5, y => 42, z => -5);

$point3d->clear('a');

こうやってみた。
すると、

Modification of a read-only value attempted at test.pl line 26.

とのことで、関数への引数は read-only でロックされてしまうようだ。

この記事のキーワード:   Moose   perl

Moose::Coockbook

各Cookbookのレシピの目次

Mooseの基礎

Moose::Cookbook::Basics::Recipe1

シンプルなPoint のMooseによる実装例。
attribute とsubclass のデモ。

Moose::Cookbook::Basics::Recipe2

シンプルな銀行口座の例。もう少し複雑なクラス。
サブクラス内のメソッドとmodifier のデモ。

Moose::Cookbook::Basics::Recipe3

ちょっといい加減な二分木の例。
いくつかのattributeの機能のデモ。型(type), weak reference, predicate(どのオブジェクトに属しているのか), 初期値、それにattributeのアクション(''uction''と書かれているが''action''のTYPO?)

Moose::Cookbook::Basics::Recipe4

副型(subtype)と単純な会社クラス構造の構築。
独自の型の作成、 ''BUILD''メソッド(コンストラクタ)、 サブクラスでのオーバーライドの使い方を紹介。

Moose::Cookbook::Basics::Recipe5

副型(subtype)と強制(coercion)をつかったRequestクラス。
型の強制(coercion)を含む型についての追加の例

Moose::Cookbook::Basics::Recipe6

内部引数(?、 The Augment/inner)についての例
''augment'' method modifireのデモ。メソッドオーバーライドのような方法を提供する

Moose::Cookbook::Basics::Recipe7

immutable を使ってMooseを高速化。
アクセサやオブジェクトの動作を劇的に高速化するimmutable class を作る

Moose::Cookbook::Basics::Recipe8

トリガを使った複合連結の管理(TODO)
(まだ説明がありません)

Moose::Cookbook::Basics::Recipe9

Builderメソッドとlazy_build
builder機能はattributeの初期値に継承可能でroleを構成できる機能を提供する

Moose::Cookbook::Basics::Recipe10

演算子のオーバーロード、副型(subtype),強制(coercion)
演算子のオーバーロード、強制、副型を目の色が決定される仕組みをモデルにしてデモします。

Moose::Cookbook::Basics::Recipe11

BUILDとBUILDARGS(TODO)
この機能を解説するちょうどいいデモを求めています!!

MooseのRole

以下のレシピはどのようにしてMooseのRoleを使うかを解説しています。

Moose::Cookbook::Roles::Recipe1

Moose::Roleの例
trait とか mix-in などとして知られている role についてのデモ。 role は コードの再利用に関して サブクラスとは別のアプローチになる方法を提供します。

Moose::Cookbook::Roles::Recipe2

より進んだ role の使いかた - メソッドの外部定義(exclude)およびエイリアス(aliasing)
roleの一部分をクラスのなかに含めたいと思うことがあるでしょう。また,roleの全体を含めたいと思ったときにいくつかのメソッドがもともとあるメソッドと衝突することがあるかもしれません。メソッドの外部定義やエイリアスを使用すればこのような問題に対処できます。

Moose::Cookbook::Roles::Recipe3

実行時role制御
(まだ説明がありません)

Meta Moose

以下のレシピは Mooseを使ったオブジェクトシステムの拡張をおこなうための meta class の書き方を解説しています。

Moose::Cookbook::Meta::Recipe1

metaの世界へようこそ(どうしてmetaが必要なの?)
もし、どうしてこのmetaなるものが面白いのか、何を心配すべきなのかと考えているのであれば、このレシピを読んでください。

Moose::Cookbook::Meta::Recipe2

meta-attribute, ラベルつきattribute
Mooseを拡張するひとつの方法は attribute metaclass を使う方法です。attribute metaclass は has によるattribute を拡張し、追加のattributeの機能を使えるようにします。

Moose::Cookbook::Meta::Recipe3

attribute trait を使ったラベルの実装
拡張されたMoose の attribute metaclass は様々な機能をもたらします。ただし、attributeはひとつのmetaclassしか持つことはできません。roleをattribute metaclassに適用することによって、attributeの機能を構成することができます。

Moose::Cookbook::Meta::Recipe4

metaclass への ''table'' attribute の追加
あなたが作成したclassにより多くの情報を詰め込みたいなら、 "Moose::Meta::Class" を拡張しましょう。方法はいたって簡単ですが、もうちょっと砂糖を入れたいと思ったときは Moose::Cookbook::Extending::Recipe2を読んでください。

Moose::Cookbook::Meta::Recipe5

metaclass trait として実装された ''table'' attribute
この例は 前のレシピで実装した class の metaclass を metaclass trait として実装したものです。

Moose::Cookbook::Meta::Recipe6

immutabilization systemにhookをかける(TODO)
Mooseには ''immutabilization'' といわれる機能があります。''__PACKAGE__->meta()->make_immutable()'' をclass(やattribute, roleなど)を定義したあとに呼び出すことで、Mooseがどのようにしてオブジェクトの生成やattributeへのアクセスなど、そういったものを最適化するかを知ることができます。
metaclassを作成したら、immutabilization systemにhookをかける必要があるかもしれません。これは metaclass class, meta method classや可能ならmeta-instance classなどを含む多くのスポットを横断します。
このレシピはimmutabilize属性の拡張をおこなう方法を示しています。

Moose::Cookbook::Meta::Recipe7

meta-instance(TODO)
(まだ説明がありません)

Mooseの拡張

これらのレシピは更なるMooseの拡張や ''MooseX''モジュールの作成の方法について書かれています。

Moose::Cookbook::Extending::Recipe1

Mooseの拡張の概要
Mooseを拡張するには、いくつもの方法があります。このレシピでは、それぞれの方法について概要を示し、その方法がどういう場合に適切かを示します。

Moose::Cookbook::Extending::Recipe2

基本object class roleの構築
多くの基本object class拡張はroleによって構築することができます。この例ではclassに抽象的なMooseX::Debuggingモジュールを使った基本object class をデバッグするroleを示します。

Moose::Cookbook::Extending::Recipe3

alternate base object class の構築
Mooseを使っているとmeta extensionを持った、alternate(代理) base object classを使いたいと思うようなことがあるかもしれません。あるいは、"extends 'MyApp::Bsse'"とtypeすることなしに別の機能を持った同じような class を使いたいと思うようなことがあるかもしれません。

Moose::Cookbook::Extending::Recipe4

Moose.pmのような、Mooseスタイルの砂糖
このレシピでは、"Moose.pm"の置き換え方法を示します。"MooseX"モジュールAPIのようなもの、特にmetaclassやbase object classのデフォルト動作を変更したい場合など。

おつまみ

Moose::Cookbook::Snack::Keywords
Moose::Cookbook::Snack::Types

この記事のキーワード:   Moose   perl

Mooseのドキュメントを読んでの覚書

Moose::Intro

  • new, DESTROYメソッドは作ってはいけない

Class

  • class は attribute を持つことができる
  • class は method を持つことができる
  • class は superclass を持つことができる
  • class は method modifier を持つことができる
  • class では role が実行されることがある
  • class は コンストラクタとデストラクタを持つことができる。(ただし、new, DESTORYという名前ではなく BUILD(), DEMOLISH() という名前のメソッドになる)
  • コンストラクタには名前つき引数を与えてインスタンスのattributeを初期化することができる
  • class は metaclassを持つ。 metaclassには meta-attribute, meta-method, meta-role が含まれる。 metaclassはインスタンスから取得できるclassの定義そのもの。

Attribute

  • attribute は read/writeフラグ, 型, アクセサメソッド, delegation, 初期値、その他を持つ。
  • attribute はそのまま参照することはできない(してはいけない)。かならずアクセサを使って参照/書換えをおこなう。

Method

  • 普通に sub で関数を作ればいい。

Role

  • Javaでいうところのinterface
  • role は attribute を持つことができる
  • role は method を持つことができる
  • role は method modifier を持つことができる
  • role は required method (Javaでいうところの abstruct function) を持つことができる
  • role を定義するには Moose::Role を use する

Method Modifier

  • method modirier をつかって、method に hook をかけることができる
  • ''before'', ''after'', ''around'', ''augment'' が使える
  • ひとつのmethodに複数のmodifierをかけることができる

型(Type)

  • attributeには型による制約を付けることができる
  • ''Str'', ''Num'', ''Bool'', ''HashRef'' などのMoose組込型がある
  • クラス名をそのまま型名として使える

Delegation

  • attribute が class のときにそのクラスのインスタンスメソッドに別名を付けてアクセスできるようにする機能(?)
  • 例えば下のように定義されているとき、$obj->date_of_last_login() は last_login->date() として機能する (で、いいのかな?)
has 'last_login' => (
  is => 'rw',
  isa => 'DateTime',
  handles => { 'date_of_last_login' => 'date' },
);

コンストラクタとデストラクタ

  • コンストラクタは BUILD() として定義する。 Moose::Object の new メソッド内から呼び出される
  • デストラクタは DEMOLISH() として定義する。

metaclass

  • metaclassを使ってインスタンスから class の定義を取得することができる。
  • Moose::Object の meta() メソッドを呼び出してそのインスタンスのmetaclass を取得できる。取得したmetaclassデータは Moose::Meta::Class オブジェクト。
2008年11月の記事一覧 < 2007年3月2009年3月 >

キーワードリスト

バックナンバー