サブルーチンの動的な生成
Perlでは、サブルーチンを動的に生成することもできます。
関数ジェネレータ
関数ジェネレータとは、サブルーチンを生成するためのサブルーチンのことです。関数ジェネレータは、動的にサブルーチンを生成したいときに用いられるテクニックのひとつです。
一般的な名称にしたがって関数ジェネレータと呼びますが、Perlの用語でいえばサブルーチンジェネレータのことです。この解説では、関数もサブルーチンも同じ意味です。
関数ジェネレータの作成と呼び出し
# 呼び出し my $func = func_generator(); $func->(); # 定義 sub func_generator { my $func = sub { print "これは、実行時に生成されたサブルーチンです。\n"; }; # サブルーチンへのリファレンスを返す。 return $func; }
関すジェネレータを作成するには、無名サブルーチンを作成して、そのリファレンスを、戻り値として返します。
関数ジェネレータを呼び出す( func_generator() )と、生成されたサブルーチンへのリファレンスが戻りち値として取得できます。生成さえたサブルーチンのリファレンスをデリファレンスして( $func->() )して、サブルーチンを呼び出します。
少しだけ異なった複数のサブルーチンを生成する
# 関数ジェネレータ呼び出し my $create_safe_msg = message_factory("is safe."); my $create_danger_msg = message_factory("is danger."); # 生成したサブルーチンの呼び出し my $safe_msg1 = $create_safe_msg->("cat"); my $danger_msg1 = $create_danger_msg->("mamushi"); # 伝えるメッセージを指定できるサブルーチンを作成する関数ジェネレータ sub message_factory { my $message = shift; return sub { my $word = shift; return $word . ' ' . $message; } }
関数ジェネレータで引数を受け取って、その引数を生成するサブルーチンの中で利用することができます。こうすることで、少しだけ異なったサブルーチンを複数生成することができます。
型グロブを使ってサブルーチンの名前を動的に決定する
型グロブを使ってサブルーチンの名前を動的に決定するサンプルです。
無名サブルーチンに名前をつける
*sum = sub { return $_[0] + $_[1]; };
型グロブを利用することで、サブルーチンに別名をつけることができます。左辺は型グロブ( *name )、右辺はサブルーチンへのリファレンス( \&subroutin )にします。
今回の例の場合は、右辺は無名サブルーチンへのリファレンスになっており、無名サブルーチンに別名をつけていることになります。
無名サブルーチンに名前をつけるくらいなら、最初から名前をつけておけばよいと考えるかもしれません。無名サブルーチンに名前をつけることの利点は、実行時にサブルーチンに名前を与えることができるということです。サブルーチン名を動的に決定することができます。
動的にサブルーチンに名前をつける
my $word = 'cat'; my $func_name = "${word}_repeat"; no strict 'refs'; *{$func_name} = sub { return "$word $word"; };
型グロブ名には、変数を使うことができます。$func_name に入る値によって、サブルーチン名が変化します。
*{$name} という記述は、use strict; を使用していると、エラーになります。no strict 'refs'として、シンボリックリンクの制限を無効にします。( ところで、*{ $name }という記述はなぜシンボリックリファレンスなんでしょうか? )今回の場合は、cat_repeat という名前を動的につけています。
もう少しよく書いてみましょう。
BEGIN { my $word = 'cat'; my $func_name = "${word}_repeat"; my $code = sub{ return "$word $word"; }; no strict 'refs'; *{$func_name} = $code; }
BEGINを使って、コンパイル時に動的にサブルーチン名を決定するようにしておきます。my $code = sub{ }; という記述でいったん無名サブルーチンへのリファレンスを変数に格納しておき、最後に *{ $func_name } = $code; として名前をつけます。このようにして、no strict 'refs' の有効範囲をできるだけ狭めておいたほうがよいです。
関数テンプレート
関数テンプレートとは、動的に複数のサブルーチンを作成するプログラム技法です。プログラムが始まる前にサブルーチンに名前をつけておくのではなくて、プログラムが始まってからサブルーチンに名前をつけます。
無名サブルーチンと型グロブを利用することで、サブルーチンを動的に生成し、動的に名前をつることができます。関数テンプレートを使えば、サブルーチン名に応じた処理を作成することができます。
関数テンプレートの書き方
my %numbers = ( one => 1, two => 2, three => 3, four => 4, ); for my $num (keys %numbers) { # 無名サブルーチンへのリファレンスを繰り返し作成 my $code = sub { # 数の名前に対応した戻り値が返る return $numbers{$num}; }; # シンボリックリファレンスの制限を解除 no strict 'refs'; # 型グロブを利用して無名サブルーチンに名前をつける。 *{$num} = $code; }
注目する点は、「サブルーチン名」と「サブルーチンの処理」に関連性があることです。サブルーチン名が、one であればそれに対応する $numbers{ 'one' },すなわち 1 が、戻り値として返却されます。
関数テンプレートを作成するには、まずforeach文にサブルーチン名の配列を渡して繰り返し呼びます。次に無名サブルーチンを作成して、関数名を利用した処理を行います。最後に、型グロブを利用して無名サブルーチンに名前をつけてあげます。
実際の利用としては、Class::Accessor などのモジュールで、アクセッサーの作成の部分で利用されています。
もう少しよく書いてみましょう。
BEGIN{ my %numbers = ( one => 1, two => 2, three => 3, four => 4, ); for my $num (keys %numbers) { # 無名サブルーチンへのリファレンスを繰り返し作成 my $code = sub { return $numbers{ $num }; # 数の名前に対応した戻り値が返る }; # シンボリックリファレンスの制限を解除 no strict 'refs'; # 型グロブを利用して無名サブルーチンに名前をつける。 *{$num} = $code; } }
BEGINブロックで囲って、コンパイル時に動的にサブルーチンを作成しておいたほうがよいです。そうしないと、この記述を行うまでは、サブルーチンを呼び出すことができません。
もっと良いのは、モジュールとして別ファイルに作成して、use を使ってコンパイル時に読み込む方法です。
サンプルコード
関数ジェネレーター
関数ジェネレーターのサンプルコードです。
関数ジェネレータをコードを使って解説します。
use strict; use warnings; print "1: 関数ジェネレータでサブルーチンを生成する\n"; my $func = func_generator(); $func->(); print "\n"; # 関数ジェネレータ sub func_generator { my $func = sub { print "これは、実行時に生成されたサブルーチンです。\n"; }; # サブルーチンへのリファレンスを返す。 return $func; } print "2: 関数ジェネレータで複数のサブルーチンを生成する。\n"; # サブルーチンへのリファレンスが返る my $create_safe_msg = message_factory("is safe."); my $create_danger_msg = message_factory("is danger."); print "'is safe' を与えて生成したサブルーチン\n"; # デリファレンスして呼び出す my $safe_msg1 = $create_safe_msg->("cat"); my $safe_msg2 = $create_safe_msg->("dog"); print "${safe_msg1}\n${safe_msg2}\n\n"; print "'is danger' を与えて生成したサブルーチン\n"; my $danger_msg1 = $create_danger_msg->("mamushi"); my $danger_msg2 = $create_danger_msg->("suzumebachi"); print "${danger_msg1}\n${danger_msg2}\n\n"; # 伝えるメッセージを指定できるサブルーチンを作成する関数ジェネレータ sub message_factory { # 関数ジェネレータの引数によって、生成するサブルーチンを動的に変更できる。 my $message = shift; return sub { my $word = shift; return $word . ' ' . $message; } }
動的に生成したサブルーチンに名前をつける
型グロブを使って、動的に生成したサブルーチンに名前をつけるサンプルです。
use strict; use warnings; # サブルーチンの名前を実行時に決定する。 # 型グロブと無名サブルーチンを利用することで実現できます。 print "1: 無名サブルーチンに名前をつける。\n"; *sum = sub { # *sum は型ブログという機能で、別名を作成することができます。 # ここでは名前のないサブルーチンにsumという名前をつけています。 return $_[0] + $_[1]; }; print "1と2の和は、" . sum(1, 2) . "です。\n\n"; print "2: 動的にサブルーチンに名前をつける。\n"; my $word = 'cat'; my $func_name = "${word}_repeat"; no strict 'refs'; *{$func_name} = sub { # 無名サブルーチンに、cat_repeat 名前を実行時に # 与えることができます。 return "$word $word"; }; my $repeat_word = cat_repeat(); print $repeat_word . "\n";
関数テンプレート
関数テンプレートを使ったサンプルです。
use strict; use warnings; # 関数テンプレート print "1: 関数テンプレートを使って動的に複数のサブルーチンを作成する。\n"; # one というサブルーチンを呼んだら 1 が返却されるようにする。 # two というサブルーチンを呼んだら 2 が返却されるようにする。 my %numbers = ( one => 1, two => 2, three => 3, four => 4, ); for my $num (keys %numbers) { # 無名サブルーチンへのリファレンスを繰り返し作成 my $code = sub { # 数の名前に対応した戻り値が返る return $numbers{ $num }; }; # シンボリックリファレンスの制限を解除 no strict 'refs'; # 型グロブを利用して無名サブルーチンに名前をつける。 *{$num} = $code; } print one(),two(),three(),four(),"\n";