Data::Util::JA(3) データとデータ型のためのユーティリティ集

VERSION

This document describes Data::Util version 0.63

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 $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

DESCRIPTION

このモジュールはデータとデータ型のためのユーティリティ関数を提供します。

ユーティリティはチェック関数群と検証関数群とその他の関数群があります。 チェック関数群は値の型を調べ,真偽値を返す機能を提供します。 検証関数群は値の型を調べ,真であればその値自身を返し, 偽であれば致命的エラーとなる機能を提供します。 その他の関数群は,無名スカラーリファレンスの生成やシンボルテーブルの操作, コードリファレンスの操作などの機能を提供します。 これらユーティリティはいずれもコードの繰り返しを避けるために設計されました。

このモジュールはXSとPure Perl双方で実装されており,Cコンパイラのある 環境ではXSバックエンドが,ない環境ではPure Perlバックエンドが使用されます。 なお,環境変数"DATA_UTIL_PUREPERL"を真に設定することで,強制的にPure Perl バックエンドを使用することができます。

XSバックエンドは注意深く実装されており, Pure Perlバックエンドより2倍から10倍程度高速に動作します。 実際,XSバックエンドが提供するほぼ全ての関数は,等価の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 [, ...])
サブルーチンsubrpackagenameとしてインストールします。

"do{ no strict 'refs'; *{$package.'::'.$name} = \&subr; }"というコードと ほぼ等価です。さらに,subrが匿名サブルーチンであれば,packageに 名前付きサブルーチン&package::nameとして命名します(ただし,Pure Perl版のコードでは匿名サブルーチンの命名は行いません)。

サブルーチンを再インストールするときは,"no warnings 'redefine'" ディレクティブを使ってください。

        no warnings 'redefine';
        install_subrouitne($package, $name => $subr);

packagenameが未定義値またはリファレンスであれば致命的エラーとなります。 subrがコードリファレンスでないときも致命的エラーとなりますが, オーバーロードマジックは考慮されます。

この関数は"no strict 'refs'"を必要としないため,strict無効化の誤謬を犯す危険性がありません。strict無効化の誤謬とは,以下のような状況を指します。

        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'">はまさにこのような誤ったシンボリックリファレンスの デリファレンスを検出するために用意されている機能なのですが,ここではその恩恵を 得ることができず,デバッグの難しいコードを生成してしまいます。

このケースでstrictの恩恵を得るためには,以下のように無名関数内で再度 "use strict"を有効にする必要があります。

        no strict 'refs';
        *{$pkg . '::' . $sub_name} = sub{
                use strict 'refs';
                my($self) = @_;
                return $self->{$property};
        }

そこで,"install_subroutine()"を使うとも"strict"を使用する必要がなくなります。

        install_subroutine $pkg => (
                $sub_name => sub{
                        my($self) = @_;
                        return $self->{$property};
                },
        );

このstrict無効化の誤謬については,``18.10'' in ``Perlベストプラクティス'' 「制約の無効化 - 制約または警告を無効にする場合は,明示的に,段階的に,最も狭いスコープで行う」 に解説があります。

uninstall_subroutine(package, name [=> code], ...)
サブルーチンnameをパッケージpackageから削除します。

"undef &subr"&subrを未定義にして型グロブのコードスロットを そのままにするのに対して,"uninstall_subroutine"は型グロブを シンボルテーブルから削除し,コードスロットを以外の値をシンボルテーブルに 戻します。 この挙動は"namespace::clean""constant::lexical"を実現するためのものです。

nameに対してcodeが与えられている場合は,&package::namecodeである 場合のみ削除します。すなわち,以下の二つのコードは等価です。

        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], ...)
サブルーチンsubrmodifier_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/Method/Modifiers.pm ("modify_subroutine()"/"subroutine_modifier()"のデモ)のベンチマーク benchmark/methext_bench.plによれば,メソッド修飾のコストはほぼ次のようになります:

        with before modifier: 100% slower
        with after  modifier: 100% slower
        with around modifier: 200% slower

特に,"before""after""SUPER::"疑似クラスによってメソッドを拡張するよりも高速です。

各修飾子については,``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は修飾子つきサブルーチン
        }

modifiedmodifier_type("before", "around", "after") を渡すと,そのmodifier_typeに応じた修飾関数を返します。

        @befores = subroutine_modifier $modified, 'before';

このほか,更に関数のリストを渡した場合には,modifiedmodifier_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] }

Error handling

検証関数によって放出される致命的エラーは,"Data::Util::Error"モジュールによって変更することができます。

        package Foo;
        use Data::Util::Error sub{ Foo::InvalidArgument->throw(@_) };
        use Data::Util qw(:validate);
        # ...

このエラーハンドラはパッケージ毎に設定され,そのパッケージ内で"Data::Util"が発生させるエラーにはそのエラーハンドラが使われます。

DISCUSSIONS

What is a X-reference?

「Xのリファレンス」とは何を指すのでしょうか。ここではハッシュリファレンスを例にとって考えます。 まず,判断要素は以下の3つを想定します。
1.
"ref($x) eq 'HASH'"
2.
"Scalar::Util::reftype($x) eq 'HASH'"
3.
"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()"も同様です。

ENVIRONMENT VARIABLES

DATA_UTIL_PUREPERL

真であれば,Pure Perl版のバックエンドが使われます。

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.

AUTHOR

Goro Fuji (gfx) <gfuji(at)cpan.org>

LICENSE AND COPYRIGHT

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.