Data::Util::JA - データとデータ型のためのユーティリティ集
This document describes Data::Util version 0.63
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 $x = 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
このモジュールはデータとデータ型のためのユーティリティ関数を提供します。
ユーティリティはチェック関数群と検証関数群とその他の関数群があります。 チェック関数群は値の型を調べ,真偽値を返す機能を提供します。 検証関数群は値の型を調べ,真であればその値自身を返し, 偽であれば致命的エラーとなる機能を提供します。 その他の関数群は,無名スカラーリファレンスの生成やシンボルテーブルの操作, コードリファレンスの操作などの機能を提供します。 これらユーティリティはいずれもコードの繰り返しを避けるために設計されました。
このモジュールはXSとPure Perl双方で実装されており,Cコンパイラのある 環境ではXSバックエンドが,ない環境ではPure Perlバックエンドが使用されます。 なお,環境変数DATA_UTIL_PUREPERLを真に設定することで,強制的にPure Perl バックエンドを使用することができます。
DATA_UTIL_PUREPERL
XSバックエンドは注意深く実装されており, Pure Perlバックエンドより2倍から10倍程度高速に動作します。 実際,XSバックエンドが提供するほぼ全ての関数は,等価のPure Perlコードを インラインで展開したコードよりも更に高速です。
ディストリビューションのbenchmark/ディレクトリにベンチマークがあります。
チェック関数群は:checkインポートタグによって導入できます。これらはある値 の型が目的の型であれば真を,そうでなければ偽を返します。
:check
また,これらの関数はオーバーロードマジックも調べます。たとえば,${}が オーバーロードされているオブジェクトは,スカラーリファレンスとして扱われます。
${}
リファレンスの型チェックをする関数は,オブジェクトリファレンスに対しては, オーバーロードされていない限り常に偽を返します。 これは,オブジェクトの実装に依存するコードを書かないようにするためです。
スカラーリファレンスかどうかのチェックを行います。
配列リファレンスかどうかのチェックを行います。
ハッシュリファレンスかどうかのチェックを行います。
コードリファレンスかどうかのチェックを行います。
グロブリファレンスかどうかのチェックを行います。
qr//によって作られる正規表現かどうかのチェックを行います。
qr//
classのインスタンスかどうかのチェックを行います。
Scalar::Util::blessed($value) && $value->isa($class)というコードと ほぼ等価です。
Scalar::Util::blessed($value) && $value->isa($class)
classが未定義値またはリファレンスであれば致命的エラーとなります。
valueに対してメソッドを起動できるかどうかをチェックします。
valueがプリミティブ値かどうかをチェックします。すなわち,定義済みであり, リファレンスではなく,型グロブでもなければ真を返します。
この関数(およびis_string/is_number()/is_integer())は, オブジェクトリファレンスに対しては常に偽を返します。 たとえvalueが文字列化/数値化/真偽値化オーバーロードメソッドを 持っていたとしても,それはプリミティブ値としては判断しません。
is_string
is_number()
is_integer()
この関数には検証を行う対応関数がありません。
valueがプリミティブ値であり, かつ文字列化したときに1文字以上の内容を持つ値かどうかをチェックします。
do{ is_value($value) && length($value) > 0 }と同じです。
do{ is_value($value) && length($value) > 0 }
valueが数値かどうかをチェックします。 ここで数値とは,数値コンテキスト(たとえばsprintf '%g', $value) で警告を出さずに数値に変換可能であり, かつPerlプログラム中にリテラルとしておくことができる値という意味です。
sprintf '%g', $value
すなわち,この関数はScalar::Util::looks_like_number()と異なり, InfinityやNaNはリテラルとしてプログラム中に置くことはできないため, 数値として扱いません。また,数値化したときに警告を出さない例外である "0 but true"も同じ理由で数値として扱いません。
Scalar::Util::looks_like_number()
Infinity
NaN
"0 but true"
valueが整数かどうかをチェックします。これはis_number()の判定に加えて, 整数値かどうかをチェックします。
検証関数は:validateタグによって導入できます。これらはチェック関数と 同じ方法でチェックを行います。 ただし,その結果が真であれば第一引数をそのまま返し, 偽であれば致命的エラーとなります。
:validate
これらの関数もオーバーロードマジックを考慮します。
スカラーリファレンスかどうかの検証を行います。
配列リファレンスかどうかの検証を行います。
ハッシュリファレンスかどうかの検証を行います。
コードリファレンスかどうかの検証を行います。
グロブリファレンスかどうかの検証を行います。
qr//によって作られる正規表現かどうかの検証を行います。
classのインスタンスかどうかの検証を行います。
valueに対してメソッドを起動できるかどうかの検証を行います。
valueがクラス名である場合,そのクラス名を正規化した文字列を返します。 すなわち,"::Foo"や"main::Foo"を与えると"Foo"を返します。
"::Foo"
"main::Foo"
"Foo"
その他,個別にインポートできるいくつかのユーティリティ関数があります。
undefを参照する匿名スカラーリファレンスを生成します。
undef
valueのコピーを参照する匿名スカラーリファレンスを生成します。
これはdo{ my $tmp = $value; \$value; }というコードと等価です。
do{ my $tmp = $value; \$value; }
valueを表示に適するよう整形した文字列を返します。 do{ defined($value) ? qq{"$value"} : 'undef' }を置き換える機能 として提供されますが,より高機能です。
do{ defined($value) ? qq{"$value"} : 'undef' }
invocantのスタッシュ stash,つまりシンボルテーブルハッシュが 存在すれば,そのスタッシュを返します。
invocantがオブジェクトリファレンスであれば,そのオブジェクトのパッケージの スタッシュを返します。
invocantがパッケージ名であり,そのパッケージが既に存在すれば, そのパッケージのスタッシュを返します。
サブルーチンsubrをpackageにnameとしてインストールします。
do{ no strict 'refs'; *{$package.'::'.$name} = \&subr; }というコードと ほぼ等価です。さらに,subrが匿名サブルーチンであれば,packageに 名前付きサブルーチン&package::nameとして命名します(ただし,Pure Perl版のコードでは匿名サブルーチンの命名は行いません)。
do{ no strict 'refs'; *{$package.'::'.$name} = \&subr; }
サブルーチンを再インストールするときは,no warnings 'redefine' ディレクティブを使ってください。
no warnings 'redefine'
no warnings 'redefine'; install_subrouitne($package, $name => $subr);
packageかnameが未定義値またはリファレンスであれば致命的エラーとなります。 subrがコードリファレンスでないときも致命的エラーとなりますが, オーバーロードマジックは考慮されます。
この関数はno strict 'refs'を必要としないため,strict無効化の誤謬を犯す危険性がありません。strict無効化の誤謬とは,以下のような状況を指します。
no strict 'refs'
my $property = ...; # ... no strict 'refs'; # simple read-only accessor *{$pkg . '::' . $sub_name} = sub{ my($self) = @_; return $self->{$property}; }
これはオブジェクトのプロパティを参照するアクセサを生成するコードです。このアクセサは,正しく使う限りでは問題はありません。 しかし,このアクセサをクラスメソッドとして呼び出すと,問題が顕在化します。 つまりそのとき$selfに入っているのはクラスを表す文字列であり, $self->{$property}というコードはシンボリックリファレンスと解釈され, このアクセサが定義されたパッケージのグローバル変数としてデリファレンスされます。 これは多くの場合,単にundefを返すだけでしょう。 <use strict 'refs'>はまさにこのような誤ったシンボリックリファレンスの デリファレンスを検出するために用意されている機能なのですが,ここではその恩恵を 得ることができず,デバッグの難しいコードを生成してしまいます。
$self
$self->{$property}
<use strict 'refs'
このケースでstrictの恩恵を得るためには,以下のように無名関数内で再度 use strictを有効にする必要があります。
use strict
no strict 'refs'; *{$pkg . '::' . $sub_name} = sub{ use strict 'refs'; my($self) = @_; return $self->{$property}; }
そこで,install_subroutine()を使うともstrictを使用する必要がなくなります。
install_subroutine()
strict
install_subroutine $pkg => ( $sub_name => sub{ my($self) = @_; return $self->{$property}; }, );
このstrict無効化の誤謬については,"18.10" in "Perlベストプラクティス" 「制約の無効化 - 制約または警告を無効にする場合は,明示的に,段階的に,最も狭いスコープで行う」 に解説があります。
サブルーチンnameをパッケージpackageから削除します。
undef &subrが&subrを未定義にして型グロブのコードスロットを そのままにするのに対して,uninstall_subroutineは型グロブを シンボルテーブルから削除し,コードスロットを以外の値をシンボルテーブルに 戻します。 この挙動はnamespace::cleanやconstant::lexicalを実現するためのものです。
undef &subr
&subr
uninstall_subroutine
namespace::clean
constant::lexical
nameに対してcodeが与えられている場合は,&package::nameがcodeである 場合のみ削除します。すなわち,以下の二つのコードは等価です。
&package::name
uninstall_subroutine($pkg, $name) if \&{$pkg . '::' . $name} == $code; uninstall_subroutine($pkg, $name => $code);
この関数はSub::Delete::delete_sub()と同じアルゴリズムに基づいていますが, 複数のサブルーチンを一度に削除できます。
Sub::Delete::delete_sub()
サブルーチンsubrのパッケージと名前のペアを返します。 これはSub::Identify::get_code_info()とほぼ同じ機能です。 ただし,スカラーコンテキストでは完全修飾名を返します。
Sub::Identify::get_code_info()
subrの名前が不明なときは,リストコンテキストでは空リストを, スカラーコンテキストではundefを返します。
\&package::nameが存在すれば,それを返します。 これはdo{ no strict 'refs'; *{$package . '::' . $name}{CODE} } に似ていますが,\&package::nameが存在しない場合でも *package::nameを生成しません。
do{ no strict 'refs'; *{$package . '::' . $name}{CODE} }
第三引数として"-create"を与えると,\&package::nameが存在しなくても スタブを生成してそれを返します。 これはdo{ no strict 'refs'; \&{$package . '::' . $name} }と同じです。
"-create"
do{ no strict 'refs'; \&{$package . '::' . $name} }
サブルーチンsubrのカリー化を行います。 つまり特定の引数を固定したクロージャを生成します。
args and/or placeholdersには,固定する引数か,カリー化サブルーチンの引数に 置き換えられるプレースホルダを渡します。プレースホルダには,添え字xを参照 する\xと,\xで参照した最大の添え字の以降の引数リストを参照する *_があります。
\x
*_
たとえば,以下の$closureと$curriedは同じ機能を持つサブルーチンとなります。
$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)が無視されます。
curry(\&subr, *_, \1)->(0, 1, 2, 3)
subr(2, 3, 1)
$_[0]
カリー化はクロージャよりも生成・呼び出しが高速です。
より詳しいサンプルコードがData::Util::Curryにあります。
サブルーチンsubrをmodifier_typeにしたがってsubroutinesで修飾し, 無名関数modified_subrとして返します。
modifier_typeにはbefore, around, afterがあり,beforeは subrの呼び出し前に,afterはsubrの呼出し後に,modified_subrに 与えられた引数で呼び出されます。beforeとafterの戻り値は捨てられます。 aroundはsubrの入出力をフィルタリングするための修飾子です。
before
around
after
その際,呼び出順は,beforeとaroundは後で定義されたものが先に呼び出され (last-defined-first-called),afterは先に定義されたものが先に呼び出されます(first-defined-first-called)。この呼び出し順はsubroutine_modifier()でも同じ です。
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/Method/Modifiers.pm (modify_subroutine()/subroutine_modifier()のデモ)のベンチマーク benchmark/methext_bench.plによれば,メソッド修飾のコストはほぼ次のようになります:
modify_subroutine()
with before modifier: 100% slower with after modifier: 100% slower with around modifier: 200% slower
特に,beforeとafterはSUPER::疑似クラスによってメソッドを拡張するよりも高速です。
SUPER::
各修飾子については,"Method Modifiers" in Class::MOP::Classに 詳しい解説があります。Class::Method::Modifiersにも解説があります。 このモジュールが提供するAPIはこれらのモジュールより低水準ですが, 機能には互換性があります。
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;
inputを元に名前と値のペア配列からなる配列リファレンスを作成します。
これはData::OptList::mkopt()に似ています。それに加えて,must_beは 名前と型のペアからなるハッシュリファレンスでもかまいません。
Data::OptList::mkopt()
For example:
$array_ref = mkopt([qw(foo bar), baz => [42]], 'moniker'); # $array_ref == [ [foo => undef], [bar => undef], baz => [42] ]
inputを元にハッシュリファレンスを作成します。
これはData::OptList::mkopt_hash()に似ています。それに加えて,must_beは 名前と型のペアからなるハッシュリファレンスでもかまいません。
Data::OptList::mkopt_hash()
$hash_ref = mkopt([qw(foo bar), baz => [42]], 'moniker'); # $hash_ref == { foo => undef, bar => undef, baz => [42] }
検証関数によって放出される致命的エラーは,Data::Util::Errorモジュールによって変更することができます。
Data::Util::Error
package Foo; use Data::Util::Error sub{ Foo::InvalidArgument->throw(@_) }; use Data::Util qw(:validate); # ...
このエラーハンドラはパッケージ毎に設定され,そのパッケージ内でData::Utilが発生させるエラーにはそのエラーハンドラが使われます。
Data::Util
「Xのリファレンス」とは何を指すのでしょうか。ここではハッシュリファレンスを例にとって考えます。 まず,判断要素は以下の3つを想定します。
ref($x) eq 'HASH'
Scalar::Util::reftype($x) eq 'HASH'
overload::Method($x, '%{}')
ref()は非常に高速なので,実用上はこれで事足りることが多いと思われます。しかし,これはオーバーロードマジックを考慮しません。
ref()
reftype()を使うべきではありません。$xがオブジェクトである場合,オブジェクトの実装型を参照し,カプセル化を壊してしまうことになるからです。
reftype()
そしてoverload::Methodが捕捉するのは,オブジェクトをある型のリファレンスとみなしてよい特殊なケースです。
overload::Method
なお,直接$xをハッシュリファレンスとみなして参照すること($x->{$key})は避けるべきです。これは$xがハッシュリファレンスでない場合に正しく致命的エラーを発生させますが,ブレスされたハッシュリファレンスのときにはアクセスが成功します。しかし,そのアクセスの成功はオブジェクトの実装に依存しています。
$x->{$key}
さて,それでは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()
Params::Util
P::U::_HASH
P::U::_HASHLIKE
したがって,is_hash_ref()ではref()とoverload::Method()を使ってリファレンスの型を調べます。is_scalar_ref(),is_array_ref(),is_code_ref(),is_glob_ref()も同様です。
overload::Method()
is_scalar_ref()
is_array_ref()
is_code_ref()
is_glob_ref()
真であれば,Pure Perl版のバックエンドが使われます。
Perl 5.10 or later.
No bugs have been reported.
Please report any bugs or feature requests to the author.
overload.
Scalar::Util.
Class::MOP.
このモジュールのいくつかの機能は以下のモジュールの機能をXSに移植して 最適化したものであり,またいくつかはそれに加えて更に拡張を施したものです。
Params::Util.
Sub::Install.
Sub::Identify.
Sub::Delete.
Sub::Curry.
Class::Method::Modifiers.
Data::OptList.
Goro Fuji (gfx) <gfuji(at)cpan.org>
Copyright (c) 2008-2009, Goro Fuji (gfx) <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.
To install Data::Util, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Data::Util
CPAN shell
perl -MCPAN -e shell install Data::Util
For more information on module installation, please visit the detailed CPAN module installation guide.