NAME
Data::Util::JA - データとデータ型のためのユーティリティ集
VERSION
This document describes Data::Util version 0.44
SYNOPSIS
use Data::Util qw(:validate);
sub foo{
# they will die if invalid values are supplied
my $sref = scalar_ref(shift);
my $aref = array_ref(shift);
my $href = hash_ref(shift);
my $cref = code_ref(shift);
my $gref = glob_ref(shift);
my $rref = regex_ref(shift);
my $obj = instance(shift, 'Foo');
# ...
}
use Data::Util qw(:check);
sub bar{
my $x = shift;
if(is_scalar_ref $x){
# $x is an array reference
}
# ...
elsif(is_instance $x, 'Foo'){
# $x is an instance of Foo
}
# ...
}
# miscelaneous
use Data::Util qw(:all);
my $ref_to_undef = anon_scalar();
$x = anon_scalar($x); # OK
my $stash = get_stash('Foo');
install_subroutine('Foo',
hello => sub{ "Hello!\n" },
goodby => sub{ "Goodby!\n" },
);
print Foo::hello(); # Hello!
my($pkg, $name) = get_code_info(\&Foo::hello); # => ('Foo', 'hello')
my $fqn = get_code_info(\&Foo::hello); # => 'Foo::Hello'
my $code = get_code_ref($fqn); # => \&Foo::hello
uninstall_subroutine('Foo', qw(hello goodby));
print neat("Hello!\n"); # => "Hello!\n"
print neat(3.14); # => 3.14
print neat(undef); # => undef
DESCRIPTION
このモジュールはデータとデータ型のためのユーティリティ関数を提供します。
ユーティリティはチェック関数群と検証関数群とその他の関数群があります。 チェック関数群は値の型を調べ,真偽値を返す機能を提供します。 検証関数群は値の型を調べ,真であればその値自身を返し, 偽であれば致命的エラーとなる機能を提供します。 その他の関数群は,無名スカラーリファレンスの生成やシンボルテーブルの操作, コードリファレンスの操作などの機能を提供します。 これらユーティリティはいずれもコードの繰り返しを避けるのに役立つはずです。
このモジュールはXSとPure Perl双方で実装されており,Cコンパイラのある 環境ではXSバックエンドが,ない環境ではPure Perlバックエンドが使用されます。
XSバックエンドは注意深く実装されていて非常に効率がよく, Pure Perlバックエンドより2倍から10倍程度高速に動作します。 また,ほぼ全ての関数は等価のPure Perlコードをインラインで展開した コードよりも高速です。
ディストリビューションのbenchmark/ディレクトリにベンチマークがあります。
INTERFACE
Check functions
チェック関数群は:check
インポートタグによって導入できます。これらはある値 の型が目的の型であれば真を,そうでなければ偽を返します。
また,これらの関数はオーバーロードマジックも調べます。たとえば,${}
が オーバーロードされているオブジェクトは,スカラーリファレンスとして扱われます。
リファレンスの型チェックをする関数は,オブジェクトリファレンスに対しては, オーバーロードされていない限り常に偽を返します。 これは,オブジェクトの実装に依存するコードを書かないようにするためです。
- is_scalar_ref(value)
-
スカラーリファレンスかどうかのチェックを行います。
- is_array_ref(value)
-
配列リファレンスかどうかのチェックを行います。
- is_hash_ref(value)
-
ハッシュリファレンスかどうかのチェックを行います。
- is_code_ref(value)
-
コードリファレンスかどうかのチェックを行います。
- is_glob_ref(value)
-
グロブリファレンスかどうかのチェックを行います。
- is_regex_ref(value)
-
qr//
によって作られる正規表現かどうかのチェックを行います。 - is_instance(value, class)
-
classのインスタンスかどうかのチェックを行います。
Scalar::Util::blessed($value) && $value->isa($class)
というコードと ほぼ等価です。classが未定義値またはリファレンスであれば致命的エラーとなります。
- is_invocant(value)
-
valueに対してメソッドを起動できるかどうかをチェックします。
- is_value(value)
-
valueがプリミティブ値かどうかをチェックします。すなわち,定義済みであり, リファレンスではなく,型グロブでもなければ真を返します。
この関数(および
is_string
/is_number()
/is_integer()
)は, オブジェクトリファレンスに対しては常に偽を返します。 たとえvalueが文字列化/数値化/真偽値化オーバーロードメソッドを 持っていたとしても,それはプリミティブ値としては判断しません。この関数には検証を行う対応関数がありません。
- is_string(value)
-
valueがプリミティブ値であり, かつ文字列化したときに1文字以上の内容を持つ値かどうかをチェックします。
do{ is_value($value) && length($value) > 0 }
と同じです。この関数には検証を行う対応関数がありません。
- is_number(value)
-
valueが数値かどうかをチェックします。 ここで数値とは,数値コンテキスト(たとえば
sprintf '%g', $value
) で警告を出さずに数値に変換可能であり, かつPerlプログラム中にリテラルとしておくことができる値という意味です。すなわち,この関数は
Scalar::Util::looks_like_number()
と異なり,Infinity
やNaN
はリテラルとしてプログラム中に置くことはできないため, 数値として扱いません。また,数値化したときに警告を出さない例外である"0 but true"
も同じ理由で数値として扱いません。この関数には検証を行う対応関数がありません。
- is_integer(value)
-
valueが整数かどうかをチェックします。これは
is_number()
の判定に加えて, 整数値かどうかをチェックします。この関数には検証を行う対応関数がありません。
Validating functions
検証関数は:validate
タグによって導入できます。これらはチェック関数と 同じ方法でチェックを行います。 ただし,その結果が真であれば第一引数をそのまま返し, 偽であれば致命的エラーとなります。
これらの関数もオーバーロードマジックを考慮します。
- scalar_ref(value)
-
スカラーリファレンスかどうかの検証を行います。
- array_ref(value)
-
配列リファレンスかどうかの検証を行います。
- hash_ref(value)
-
ハッシュリファレンスかどうかの検証を行います。
- code_ref(value)
-
コードリファレンスかどうかの検証を行います。
- glob_ref(value)
-
グロブリファレンスかどうかの検証を行います。
- regex_ref(value)
-
qr//
によって作られる正規表現かどうかの検証を行います。 - instance(value, class)
-
classのインスタンスかどうかの検証を行います。
classが未定義値またはリファレンスであれば致命的エラーとなります。
- invocant(value)
-
valueに対してメソッドを起動できるかどうかの検証を行います。
valueがクラス名である場合,そのクラス名を正規化した文字列を返します。 すなわち,
"::Foo"
や"main::Foo"
を与えると"Foo"
を返します。
Micellaneous utilities
その他,個別にインポートできるいくつかのユーティリティ関数があります。
- anon_scalar()
-
undef
を参照する匿名スカラーリファレンスを生成します。 - anon_scalar(value)
-
valueのコピーを参照する匿名スカラーリファレンスを生成します。
これは
do{ my $tmp = $value; \$value; }
というコードと等価です。 - neat(value)
-
valueを表示に適するよう整形した文字列を返します。
do{ defined($value) ? qq{"$value"} : 'undef' }
を置き換える機能 として提供されますが,より高機能です。 - get_stash(invocant)
-
invodantのスタッシュ stash,つまりシンボルテーブルハッシュが 存在すれば,そのスタッシュを返します。
invocantがオブジェクトリファレンスであれば,そのオブジェクトのパッケージの スタッシュを返します。
invocantがパッケージ名であり,そのパッケージが既に存在すれば, そのパッケージのスタッシュを返します。
- install_subroutine(package, name => subr [, ...])
-
サブルーチンsubrをpackageにnameとしてインストールします。
do{ no strict 'refs'; *{$package.'::'.$name} = \&subr; }
というコードと ほぼ等価です。さらに,subrが匿名サブルーチンであれば,packageに 名前付きサブルーチン&package::nameとして命名します。サブルーチンを再インストールするときは,
no warnings 'redefine'
ディレクティブを使ってください。no warnings 'redefine'; install_subrouitne($package, $name => $subr);
packageかnameが未定義値またはリファレンスであれば致命的エラーとなります。 subrがコードリファレンスでないときも致命的エラーとなりますが, オーバーロードマジックは考慮されます。
なお,Pure Perl版のコードでは匿名サブルーチンの命名は行いません。
- uninstall_subroutine(package, name [=> code], ...)
-
サブルーチンnameをパッケージpackageから削除します。
undef &subr
が&subr
を未定義にして型グロブのコードスロットを そのままにするのに対して,uninstall_subroutine
は型グロブを シンボルテーブルから削除し,コードスロットを以外の値をシンボルテーブルに 戻します。 この挙動はnamespace::clean
やconstant::lexical
を実現するためのものです。nameに対してcodeが与えられている場合は,
&package::name
がcodeである 場合のみ削除します。すなわち,以下の二つのコードは等価です。uninstall_subroutine($pkg, $name) if \&{$pkg . '::' . $name} == $code; uninstall_subroutine($pkg, $name => $code);
この関数は
Sub::Delete::delete_sub()
と同じアルゴリズムに基づいていますが, 複数のサブルーチンを一度に削除できます。 - get_code_info(subr)
-
サブルーチンsubrのパッケージと名前のペアを返します。 これは
Sub::Identify::get_code_info()
とほぼ同じ機能です。 ただし,スカラーコンテキストでは完全修飾名を返します。subrの名前が不明なときは,リストコンテキストでは空リストを, スカラーコンテキストでは
undef
を返します。 - get_code_ref(package, name)
-
\&package::nameが存在すれば,それを返します。 これは
do{ no strict 'refs'; *{$package . '::' . $name}{CODE} }
に似ていますが,\&package::nameが存在しない場合でも *package::nameを生成しません。第三引数として
"-create"
を与えると,\&package::nameが存在しなくても スタブを生成してそれを返します。 これはdo{ no strict 'refs'; \&{$package . '::' . $name} }
と同じです。 - curry(subr, args and/or placeholders)
-
サブルーチンsubrのカリー化を行います。 つまり特定の引数を固定したクロージャを生成します。
args and/or placeholdersには,固定する引数か,カリー化サブルーチンの引数に 置き換えられるプレースホルダを渡します。プレースホルダには,添え字xを参照 する
\x
と,\x
で参照した最大の添え字の以降の引数リストを参照する*_
があります。たとえば,以下の
$closure
と$curried
は同じ機能を持つサブルーチンとなります。my $class = 'Foo'; $closure = sub{ is_instance($_[0], $class) }; $curried = curry \&is_instance, \0, $class; $closure = sub{ install_subroutine($class, @_) }; $curried = curry \&install_subroutine, $class, *_;
なお,
*_
は\x
で参照しなかった引数リストではないので注意してください。 たとえば,curry(\&subr, *_, \1)->(0, 1, 2, 3)
というカリー化では,subr(2, 3, 1)
が呼び出され,カリー化サブルーチンに与えられた$_[0]
(つまり0)が無視されます。より詳しいサンプルコードがData::Util::Curryにあります。
- modify_subroutine(subr, modifier_type => [subroutines], ...)
-
サブルーチンsubrをmodifier_typeにしたがってsubroutinesで修飾し, 無名関数modified_subrとして返します。
modifier_typeには
before
,around
,after
があり,before
は subrの呼び出し前に,after
はsubrの呼出し後に,modified_subrに 与えられた引数で呼び出されます。before
とafter
の戻り値は捨てられます。around
はsubrの入出力をフィルタリングするための修飾子です。その際,呼び出順は,
before
とaround
は後で定義されたものが先に呼び出され (last-defined-first-called),after
は先に定義されたものが先に呼び出されます(first-defined-first-called)。この呼び出し順はsubroutine_modifier()
でも同じ です。たとえば:
$modified = modify_subroutine(\&foo, around => [sub{ my $next = shift; do_something(); goto &{$next}; # continuation }]); $modified->(); $modified = modify_subroutine(\&foo, before => \@befores, around => \@arounds, after => \@afters, ); $modified->();
XSによる実装では,サブルーチン修飾子のコストが非常に安くなっています。
このディストリビューションに付属しているexample/lib/MethodModifiers (
modify_subroutine()
/subroutine_modifier()
のデモ)のベンチマーク benchmark/methext_bench.plによれば,メソッド修飾のコストはほぼ次のようになります:with before modifier: 100% slower with after modifier: 100% slower with around modifier: 200% slower
なお,
SUPER::
疑似クラスによるメソッドの拡張は約500% slowerですから, メソッド修飾子による拡張の方が数倍高速です。各修飾子については,"Method Modifiers" in Class::MOP::Classに 詳しい解説があります。Class::Method::Modifiersにも解説があります。 このモジュールが提供するAPIはこれらのモジュールより低水準ですが, 機能には互換性があります。
- subroutine_modifier(modified, modifier_type => subroutines, ...)
-
modify_subroutine()
で生成したmodifiedを操作します。引数をmodifiedのみ渡した場合は,そのmodifiedが
modify_subroutine()
で 生成されたものかどうかを示す真偽値を返します。if(subroutine_modifier $subr){ # $subrは修飾子つきサブルーチン }
modifiedとmodifier_type(
before
,around
,after
) を渡すと,そのmodifier_typeに応じた修飾関数を返します。@befores = subroutine_modifier $modified, 'before';
このほか,更に関数のリストを渡した場合には,modifiedのmodifier_typeに その関数を追加します。
subroutine_modifier $modified, before => @befores;
- mkopt(input, moniker, require_unique, must_be)
-
inputを元に名前と値のペア配列からなる配列リファレンスを作成します。
これは
Data::OptList::mkopt()
に似ています。それに加えて,must_beは 名前と型のペアからなるハッシュリファレンスでもかまいません。For example:
$array_ref = mkopt([qw(foo bar), baz => [42]], 'moniker'); # $array_ref == [ [foo => undef], [bar => undef], baz => [42] ]
- mkopt_hash(input, moniker, must_be)
-
inputを元にハッシュリファレンスを作成します。
これは
Data::OptList::mkopt_hash()
に似ています。それに加えて,must_beは 名前と型のペアからなるハッシュリファレンスでもかまいません。For example:
$hash_ref = mkopt([qw(foo bar), baz => [42]], 'moniker'); # $hash_ref == { foo => undef, bar => undef, baz => [42] }
DISCUSSIONS
What is a X-reference?
「Xのリファレンス」とは何を指すのでしょうか。ここではハッシュリファレンスを例にとって考えます。 まず,判断要素は以下の3つを想定します。
ref($x) eq 'HASH'
Scalar::Util::reftype($x) eq 'HASH'
overload::Method($x, '%{}')
ref()
は非常に高速なので,実用上はこれで事足りることが多いと思われます。しかし,これはオーバーロードマジックを考慮しません。
reftype()
を使うべきではありません。$xがオブジェクトである場合,オブジェクトの実装型を参照し,カプセル化を壊してしまうことになるからです。
そしてoverload::Method
が捕捉するのは,オブジェクトをある型のリファレンスとみなしてよい特殊なケースです。
なお,直接$xをハッシュリファレンスとみなして参照すること($x->{$key}
)は避けるべきです。これは$xがハッシュリファレンスでない場合に正しく致命的エラーを発生させますが,ブレスされたハッシュリファレンスのときにはアクセスが成功します。しかし,そのアクセスの成功はオブジェクトの実装に依存しています。
さて,それではis_hash_ref()
は何を調べればいいのでしょうか。回答の一つはParams::Util
が示しています。Version 0.35の時点では,P::U::_HASH
は(1)を,P::U::_HASHLIKE
は(2)と(3)をチェックします。しかし先に述べたように,(1)は高速ですがオーバーロードマジックを考慮しないので不完全であり,(2)はオブジェクトのカプセル化を壊すため使うべきではありません。このように考えると,is_hash_ref()
は(1)と(3)によるチェックを行うのが正しい実装ということになります。
したがって,is_hash_ref()
ではref()
とoverload::Method()
を使ってリファレンスの型を調べます。is_scalar_ref()
,is_array_ref()
,is_code_ref()
,is_glob_ref()
も同様です。
DEPENDENCIES
Perl 5.8.1 or later.
BUGS AND LIMITATIONS
No bugs have been reported.
Please report any bugs or feature requests to the author.
SEE ALSO
このモジュールのいくつかの機能は以下のモジュールの機能をXSに移植して 最適化したものであり,またいくつかはそれに加えて更に拡張を施したものです。
AUTHOR
Goro Fuji <gfuji(at)cpan.org>
LICENSE AND COPYRIGHT
Copyright (c) 2008, Goro Fuji <gfuji(at)cpan.org>. Some rights reserved.
This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.