スポンサーサイト

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

上記の広告は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
スポンサーサイト

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

2006年08月25日 18:07

前回、どこまで書いたっけかなw

・・・・・前回の解説じゃちょっと飛んでるところも多いね、こりゃ。

まず、用語説明から。
 ・TCClosure  クロージャー(Closure)を実現するために あるけ が作ったテンプレートクラス
 ・Lambda   無名関数を扱えるようにした BOOST に入ってるトリッキーなテンプレートクラス
こんなもんかな。

他にはカリー化とかも知っておいた方がいいんだろうけど、あるけ 自体実は良くわかってないので省きますw



さて、前回は式のオブジェクト化を行えば Closure が出来そうなこととか、Lambda がどうやって動いてるのか?を簡単に説明しました。

Lambda 自体はすごく綺麗に動的に関数の生成を行えるように作られています。
しかし、実際問題として、Lambda を有機的に使うことはすごく難しいです。
なぜなら、この前の説明で作った ((_1 * 100 + _2 + 10)(1,2)) の左辺値を変数に格納しようとしたら、

boost::lambda::lambda_functor_base::sig::type boost::lambda::lambda_functor::operator ()(A &,B &) const'
 with
  Act=boost::lambda::arithmetic_action,
  Args=boost::tuples::tuple    arithmetic_action,boost::tuples::tuple    lambda_functor,
    boost::tuples::tuple>,
    const boost::lambda::detail::parameter_traits_    IF::RET>::type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::
    null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,
    boost::tuples::null_type>>>,boost::lambda::lambda_functor>,boost::tuples::null_type,
    boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,
    boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type>>>,const boost::lambda::detail::
    parameter_traits_::RET>::type,boost::
    tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::
    null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type>,
  SigArgs=boost::tuples::tuple    null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,
    boost::tuples::null_type>,
  T=boost::lambda::lambda_functor_base,boost::
    tuples::tuple    lambda_functor,
    boost::tuples::tuple    arithmetic_action,boost::tuples::tuple    lambda::placeholder<1>>,const boost::lambda::detail::parameter_traits_    IF::RET>::type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::
    null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,
    boost::tuples::null_type>>>,boost::lambda::lambda_functor>,boost::tuples::null_type,
    boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::
    tuples::null_type,boost::tuples::null_type,boost::tuples::null_type>>>,const boost::lambda::detail::
    parameter_traits_::RET>::type,boost::
    tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::
    null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type>>,
  A=int,
  B=int

まぁ、こんな冗談みたいな長さのテンプレートを宣言しないと格納できないわけです(;´Д`)'`ァ'`ァ

これが Lambda を使用する場合のもっともとっつきにくい部分でしょう。
使える場面が少ないのと、使う場合は必ずテンプレート関数にしないといけないからです。
暗黙のテンプレート引数として使えば、別にどんな型だろうが使う側は知らなくてもOKですからね。


次は、Lambda がどうやって動いてるのかを説明しようと思います。
今日は忙しいのでさわりの部分だけw


blog_rank



最近の記事


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