Hatena::ブログ(Diary)

bingo_nakanishiの他言語出身者のためのPerl入門

2009-02-11

第57回 コールバックをもっとていねいに

10:59 | 第57回 コールバックをもっとていねいにを含むブックマーク

前回、クロージャの話を書いたが、あまりていねいに説明はしなかった、

なぜかと言うと、そんなに実践で使われていないから(と私は感じているから)。


でも、詳しく知りたい方がおられるようなので、ていねいに解説してみよう。

まず、クロージャの前に、いろいろな前提知識を復習もかねて、書いていこう。


関数を後で使う

遅延評価(ちえんひょうか)という言葉がある。関数型言語は詳しくないので、

404 Blog Not Found:λ Calculus - まずは遅延評価から

を参考にしてみる。


嘘になってしまうかもしれないが、遅延評価とは読んで字のごとく遅く評価したいのだ。

言い換えると「後で評価したい」。


関数へのリファレンス

いままで、無名関数を使って関数へのリファレンスを取得する方法を見てきたが、

配列は、

\@c

ハッシュは、

\%c

リファレンスを取得できるように、

関数

\&c

という形でリファレンスを取得できる。

やってみよう。

use strict;

sub hoge{
  print '評価された!', "\n";
}

my $h = \&hoge;

print 'リファレンスで呼び出す', "\n";
$h->();

結果:

リファレンスで呼び出す
評価された!

結果を見ればわかるが、関数hogeが実行されるのは、

$h->();

の部分であって

my $h = \&hoge;

の部分ではない。

つまり、

\&hoge

では、まだ関数は評価されていないと考えることができる。

関数が評価されるのは、まだもっとあと、

$h->()

が書かれている部分まで待たなければならない。

これを遅延評価と考えよう。


個人的には、遅延評価の考え方とはたぶんちょっと違うと思うので、

私はこれを、「関数をあとで発動させる」と呼んでいる。



関数関数を渡す

これを一歩おし進めて、関数関数を渡す処理を考えてみる。

fooという関数を書いてみたのでじっくり読んでみてほしい。

use strict;

sub hoge{
  print '評価された!', "\n";
}

sub foo{
  my $code = shift;

  print 'fooの中です', "\n";
  $code->();
  print 'fooの中を出ます', "\n";
}

foo(\&hoge);

結果:

fooの中です
評価された!
fooの中を出ます

foo関数に、hoge関数を与えたのである。

hoge関数をいつ発動(評価)するかは、foo関数に任せる。

言ってしまうと、いつ渡した関数が発動されるのかは、foo関数作者に聞いてみないと(もしくはfoo関数を読んでみないと)わからないのだ。


コールバック

コールバックとは、プログラム界隈の言葉でない使われ方、たんなる日常的に使われる単語での意味としては、

「電話をかけ直す」

という意味である。

あなたの携帯に電話がかかってきたとしよう。

あなたは受話器を取ったが、今は外でメモをとる準備ができていなかったとする。

そういうときは、家に帰ってからまた「かけなおすよ」と言うだろう。

この行為が「コールバック(かけなおす)」なのだ。


プログラム界隈においては、この発想を進めて、関数を後で発動させる意味合いになる。

いつ発動させるかは、電話の場合と同じで、かけなおす方に依存する。

「10分後にかけなおす」

といえば、10分後に電話がかかってくるのが予測できるように、

HTTPのレスポンスがあったら、コールバックする」

という仕様なら、HTTPのレスポンスがあった瞬間にあなたが与えた関数が発動するという仕組みなのである。


まとめ

関数が、関数を受け取ってその受け取った関数を発動させれば、

それはコールバックだ。


しかし、ただ受け取ったものを発動させるだけでは有意義なプログラムとは言えないだろう。コールバックの使いどころとしては、


何かが起こったら発動させる

何かの条件で発動させる


などが考えられる。


また、

前回のhtz氏のコメントにあるprototype.js的なeach文のコールバックは、

my_foreach(配列, 関数)

という形をしている。

これは、第1引数配列と、その配列を処理するための関数を第2引数に渡している。

このやり方では、コールバックは、与えられた関数(処理と考えよう)を発動(実行)させるために使われている。


次回は、クロージャをもっとていねいに

を予定。