スポンサーサイト

--年--月--日 --:--

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

C++言語で関数型プログラミング 第2章

2006年09月22日 13:56

pelt の記事を挟んじゃったけど、クロージャーの記事の続きです。

前回は Lambda が非常に長ったらしいテンプレート引数を持ったクラスだってことと、テンプレートが複雑に絡み合って動作していることを書きました。



今回は Lambda がどうやって動いてるかを具体的に説明したいと思います。

まず Lambda の動作。
Lambda とは無名関数を扱うためのテンプレートです。
printf( "%d\n", (_1 * 10)(5) )
と書いたとき、50 と表示されるわけです。

何が無名なのか?
それは _1 を後で 5 と置き換えてるところです。
要するに、この 5 と書いてるところは何でもいれていいわけです。

実際、このまま使うだけでは大した威力もありません。
これがテンプレート関数と結びつくと色々面白いことが出来ます。

template int Test( T lambda )
{
  return lambda(7);
}

として
 Test( _1 * 10 )
を呼んでやると

結果が 70 と帰ってきます。
要するに、式を渡すことでパラメーターが帰るようになるわけです。

画期的だと思いませんか?
従来はパラメーターを渡すことで中で式に代入し、結果が返ってきました。
これだとパラメーターが同じ場合、式を取り替えるのは呼ぶ関数を変更することになります。

たとえば、曲線の補間処理を行う時、従来は曲線タイプを保持して計算処理を選択・実行していたところ、式自体を保持できるので曲線式自体をパラメーターとして保持できます。
曲線タイプを保持するより、よほど柔軟に処理できますよね。
ユーザーが自分で定義した曲線も処理できます。
(実際は数式しか保存できないので、曲線を定義するのは難しい)

他には、クイックソートの比較条件などを渡してやるなど、色々応用範囲はあります。



さて、実際どうやって実現してるかというと、、、TypeList というテンプレートがあります。
これ自体は

template< typename T, typename U >
struct TypeList
{
  typedef typename T Head;
  typedef typename U Tail;
};

と、まぁ、これだけの構造体なのですが、これを使用することで無限にテンプレートの引数を増やすことが出来ます。
要するに、TypeList を入れ子状にするわけです。

 TypeList< int, TypeList< float, double > >

などのように。
こうすれば、何個でもテンプレートの引数を増やすことが出来ます。

上のタイプは型の定義しかしていませんが、これに実体を持たせたものが Lambda で使用されています。



template< typename T, typename U >
struct Tuple
{
  typedef typename T Head;
  typedef typename U Tail;
  
  Head  h;
  Tail  t;
};


実体を持つことで実際にデータを保持することが出来、柔軟に(自動的に)型が決定される Lambda に使われるわけです。

さて、どうやって実装されているのか?
ためしに実装したのがありますのでこれを元に説明します。


ってことで、下にファイルを添付します。

LambdaSample1.txt


実行してみるとわかると思いますが、テンプレートが式を保存出来てることがわかると思います。

さて、この技術の肝となる部分は、、、

#define LAMBDA_OPERATOR_BINARY_TL( en, op )               \
  template< ActionType Act, typename Arg, typename B > inline     \
  Lambda< en, Tuple< Lambda, B > >             \
  op( const Lambda &a, const B &b )             \
  { return Lambda, B > >( a, b ); }   \

#define LAMBDA_OPERATOR_BINARY_LT( en, op )               \
  template< typename A, ActionType Act, typename Arg > inline     \
  Lambda< en, Tuple< A, Lambda > >             \
  op( const A &a, const Lambda &b )             \
  { return Lambda > >( a, b ); }   \

#define LAMBDA_OPERATOR_BINARY_LL( en, op )                        \
  template< ActionType ActA, typename ArgA, ActionType ActB, typename ArgB > inline   \
  Lambda< en, Tuple< Lambda, Lambda > >             \
  op( const Lambda &a, const Lambda &b )            \
  { return Lambda, Lambda > >( a, b ); }  \

#define LAMBDA_OPERATOR_BINARY( en, op )    \
  LAMBDA_OPERATOR_BINARY_TL( en, op )    \
  LAMBDA_OPERATOR_BINARY_LT( en, op )    \
  LAMBDA_OPERATOR_BINARY_LL( en, op )    \
  


LAMBDA_OPERATOR_BINARY( PLUS, operator + )
LAMBDA_OPERATOR_BINARY( MINUS, operator - )
LAMBDA_OPERATOR_BINARY( MULTIPLE, operator * )
LAMBDA_OPERATOR_BINARY( DIVIDE, operator / )
LAMBDA_OPERATOR_BINARY( SURPLUS, operator % )


この部分です。

やってることは、オペレーターの実装をしているんですけど、戻り値に Tuple を使用し新しい型を定義して入れ子で返してるのがわかると思います。
要するに、Lambda を + や * などのオペレーターで合成するたびに新しい型を戻り値の型として返してるワケです。


あるけ がテストで書いた、この Lambda 、機能としては全然ダメです。
戻り値が一番最初に突っ込んだ型に特定されちゃうこと
(たとえば上のファイルでいうと、(10 + sample1)() の場合、必ず戻り値は int になってしまう)
が、上げられます。


しかし、ほぼ BOOST の Lambda も同じようなトリックで動いています。
上の式に Unary(単項) と Binary(二項) の定義を追加し、引数を評価できるようにトリックを入れてやれば BOOST の Lambda が出来上がります。


次回は、それじゃ あるけ が作った TCClosure はどういった原理で動いてるの?を説明します。

あるけ が作った TCClosure は戻り値の型はかなり柔軟に対応されています。
また、今回 あるけ が書いた Lambda や BOOST の Lambda と違い、「戻り値の型」だけ指定してやれば式を「変数として保存する」ことも可能です。

BOOST の Lambda では式の保存を行うことはテンプレート関数やクラス以外で無理でした。

次回はもう少し具体的な話が出来ると思います。




blog_rank
スポンサーサイト


コメント

    コメントの投稿

    (コメント編集・削除に必要)
    (管理者にだけ表示を許可する)

    トラックバック

    この記事のトラックバックURL
    http://angra.blog31.fc2.com/tb.php/74-a8fab753
    この記事へのトラックバック


    最近の記事


    上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。