●Perl5編 第28章 クロージャ

○無名関数

無名配列や無名ハッシュが存在するように、無名関数(無名サブルーチン)というものも あります。名前が無いのですから、やはりリファレンスを経由してアクセスする以外の使い道がありません。 無名関数は、次のように使います。

#!C:/Perl/bin/perl

use strict;
use warnings;


# 無名関数へのリファレンス
my $ref_func = sub{
	print "OK!!\n";
};

# リファレンス経由で呼び出す
$ref_func->();

それほど難しくはなく、大体、想像通りの方法で記述できます。リファレンス経由での呼び出しの際 には、->演算子が使えます。

無名関数が引数や戻り値を持つことも当然可能です。

#!C:/Perl/bin/perl

use strict;
use warnings;


# 無名関数へのリファレンス
my $ref_func = sub{
	my $str = shift;
	print "$str\n";
};

# リファレンス経由で呼び出す
$ref_func->( 'test' );

これも難しくはないと思います。本番はここからです。

○クロージャ

無名関数が登場したら、クロージャについて説明しておく必要があるでしょう。まずは、次のサンプル スクリプトを見てください。

#!C:/Perl/bin/perl

use strict;
use warnings;


my $ref_func;

# ここから新しいスコープ
{
	my $number = 0;  # スコープ内の my変数
	$ref_func = sub{
		my $str = shift;
		
		$number++;
		print "$number: $str\n";
	};
} # このスコープを抜けるとき、$number も消えてしまうはず

#$number = 0;  # この文を有効にすると実行できない

$ref_func->( 'abcde' );
$ref_func->( 'AAA' );
$ref_func->( 'xyz' );

スコープを1つ作り、その内部に $number という my変数を定義しています。スコープの外側から、内側 の変数をアクセスすることはできない(スコープを抜けるときに、値は消えてしまう)ので、スコープ外に あるコメント文( $number = 0; ) を有効にすると、実行できなくなります。

ところが、無名関数の内部では、この $number にアクセスしています。この無名関数の定義を書いている 場所からは、$number の定義が見えるので、何の問題も無いようですが、この無名関数のリファレンスを手に 入れて、$number のスコープ外から(リファレンス経由で)呼び出したらどうなってしまうのでしょう?

実行してみれば分かるように、何の問題もなく動作します。出力されるのは、

1: abcde
2: AAA
3: xyz

です。$number の値が、確かにインクリメントされていることが分かります。

このサンプルで分かるように、レキシカルスコープ内で定義された無名関数は、リファレンス経由でアクセス されるとき、例え定義されたスコープとは異なる場所から呼び出されたとしても、定義の書いてあるスコープ 内で実行されたことになります。これがクロージャという考え方です。



C言語に無名関数はありませんが、クロージャ的なことはできます。C言語には static変数というものが あるので、これを使えば同じようなことができます。

#include <stdio.h>

/* プロトタイプ */
void function(const char* str);


/* メイン関数 */
int main(void)
{
	typedef void (*ref_func)(const char* str);  /* 関数ポインタの型 */

	ref_func p = function;  /* 実験用関数へのポインタ */

	/* ポインタ経由で関数呼び出し */
	p( "abcde" );
	p( "AAA" );
	p( "xyz" );

	return 0;
}

/* 実験用関数 */
void function(const char* str)
{
	static int number = 0;

	number++;
	printf( "%d: %s\n", number, str );
}

無名関数へのリファレンスの代わりに、関数ポインタが使えます。これだと、function() を直接呼び出せて しまうのが、Perl の無名関数との差でしょうか。後は、前述したように、スコープ内の my変数の代わりに、 関数内の staticローカル変数を利用します。


Perl5編のトップページに戻る

サイトのトップページに戻る