2012-09-06

SCIP in C++11 ― 2.4節その1



複素数とパッケージ

ここで言うパっケージを表現するのは、C++ではまあクラスだわな。
抽象クラスでインターフェースを定義して、
継承で具体的に実装する話と思っていいのだろう。
オブジェクト指向はあくまで、数あるデータ/手続き構造の表現の中の一つに過ぎない、
というのも今までこの本で学んだことだが、ここではオブジェクト指向が向いていそう。
クラスでなくnamespaceでもいいような気がするが、まずは慣れ親しんだクラスで。

getputの部分はかなり問題。
本格的には3.3.3節でしっかり実装されるわけだが、
C++の関数の型の扱いが重くのしかかる。
関数をこういう扱いするときは、lispの表現力の高さが感じられるなあ。
ちょうどGrahamの「On Lisp」を読み始め、
ついでに「Beating the Averages」を読んだところで、
警戒すべきライバルとそうでないところとを見分けられるようになった。
... C++プログラマを募集しているところも安全だ。」
とか言われると、自分はC++をこちょこちょいじくって何をやっているんだろうと不安になる。
実際作っていて、殆どの作業は型インターフェースの調整に費やされているわけで、
Schemeではこんなの考えなくていいのに・・・
型があるから、何をしているかSchemeより分かりやすいというのはあるのだが、
本当に考えるべきなのは、型の調整なんかじゃないんじゃないんだろうか」
という思いがふつふつと湧く。

putget3.3.3節で、メッセージパッシングを使った、
意外な形の実装がしっかりとなされるが、
ここでは面倒なので敢えてそこまでしないで、
3.3.3節との比較のために、もっと単純なテーブル管理クラスを作って、
グローバルスコープに置いておく。

またListの内部実装に、関数を格納するクラスを継承で実装。
実行できる形でリスト内から関数を取り出すため、
executable関数を作ったが、そのテンプレートパラメターがウザい。
今のところ関数の型自体は動的生成というわけでないので、
コンパイル時に解決できるはずだし、実行時にRTTIで試してみても、
関数の型情報はちゃんと伝わっている。
だから少なくともテンプレートパラメタについては、なくても済ませられるはずだが、
まずは動くものを作っておこう。

apply-generic内のSchemeapplyを表現するには、
C++では任意個のplaceholderに対して、
bindで再帰的な部分適用をすることが必要だが、
これはC++11を持ってしても難しい・・・。
よく見ればapply自体の表現はapply-genericで必須ではないのだが、
どの道個々のargsからタグをcontentsで引っ剥がした引数リストを、
getの結果帰ってくる関数に渡さにゃいかんわけで、
任意個の場合にこれはどうすればいいものやら・・・lispの表現力すごいなあ。
さしあたっては、実際のargsは、
複素数(や有理数・有理関数)の引数が1個か2個の時が多分殆どだろうから、
args1個の時と2個の時を別々に書いてオーバーロードすることにする。
いずれ任意個のが必要になる時が来るのかなあ・・・。

メンバ関数をfunctionラッパに渡すには、
一度クロージャでキャプらないといけないらしい。
クロージャはまだ慣れないだけに安全性が心配になる。
しかしクロージャでキャプったメンバ関数の、元のクラスを試しにdeleteしてみると、
make-from-real-imagは死んだが、
deleteされたあとでもreal-partは動いている。
謎の振る舞いだが、多分コンパイラの最適化が絡んできていそうだなぁ。

----
template<typename ReturnType, typename ...ArgTypes>
const List makeLeaf(const function<ReturnType(ArgTypes...)>& proc){
    return(make_shared<FunctionLeaf<ReturnType,ArgTypes...>>(proc));
}

template<typename ReturnType,typename...ArgTypes>
class FunctionLeaf:public ListTree{
public:
    FunctionLeaf(void)=delete;
    FunctionLeaf(const function<ReturnType(ArgTypes...)>& procedureIn)
        :ListTree(nullptr),procedure(procedureIn){}
    const ReturnType operator()(const ArgTypes... args)const{
        return(this->procedure(args...));
    }
    virtual const bool isFunction(void)const{return(true);}
private:
    const function<ReturnType(ArgTypes...)> procedure;
};

template <typename ReturnType,typename ... ArgTypes>
const function<ReturnType(ArgTypes...)>
executable(const List& leafFunction)
{
    return(*dynamic_pointer_cast<FunctionLeaf<ReturnType,ArgTypes...>>
           (leafFunction));
}


//---------abstraction barrier---------
map<string,List> table;

void put(const string operation, const string type,
         const List& functionItem)
{
    table.insert(make_pair(operation+type,functionItem));
}

const List get(const string operation,const string type)
{
    auto position=table.find(operation+type);
    if(position==table.end()){
        return(makeList());
    }
    return(position->second);
}

//---------abstraction barrier---------
typedef string TagType;

const List attachTag(const TagType typeTag,const List& contents)
{return(cons(makeLeaf(typeTag),contents));}

const TagType typeTag(const List& datum)
{
    if(isPair(datum)){return(value<TagType>(car(datum)));}
    cerr<<"Bad tagged datum -- TYPE-TAG"<<listString(datum)<<endl;
    exit(1);
    return("");
}

const List contents(const List& datum)
{
    if(isPair(datum)){return(cdr(datum));}
    cerr<<"Bad tagged datum -- CONTENTS"<<listString(datum)<<endl;
    exit(1);
    return(makeList());
}

const List applyGeneric
(const string operation, const List& arg1)
{
    const TagType typeTag1(typeTag(arg1));
    const List procedureLeaf(get(operation,typeTag1));
    if(isFunction(procedureLeaf)){
        const auto procedure(executable<List,List>(procedureLeaf));
        return(procedure(contents(arg1)));
    }
    cerr<<"No method for these types --- APPLY-Generic"
        <<listString(makeList(operation,arg1))<<endl;
    return(makeList());
}

const List applyGeneric
(const string operation, const List& arg1, const List& arg2)
{
    const TagType typeTag1(typeTag(arg1));
    const List procedureLeaf(get(operation,typeTag1));
    if(isFunction(procedureLeaf)){
        const auto procedure(executable<List,List,List>(procedureLeaf));
        return(procedure(contents(arg1),contents(arg2)));
    }
    cerr<<"No method for these types --- APPLY-Generic"
        <<listString(makeList(operation,arg1,arg2))<<endl;
    return(makeList());
}

//---------abstraction barrier---------
typedef List Complex;

const bool isRectangular(const Complex z)
{return(isEq(typeTag(z),makeLeaf("rectangular")));}


const bool isPolar(const Complex z)
{return(isEq(typeTag(z),makeLeaf("polar")));}

class ComplexProcedure{
public:
    ComplexProcedure(void)=delete;
    ComplexProcedure(const TagType tagIn):tagString(tagIn){}
    virtual ~ComplexProcedure(void){};
   
    virtual const Complex realPart(const Complex&)const=0;
    virtual const Complex imagPart(const Complex&)const=0;
    virtual const Complex makeFromRealImag
    (const Complex&, const Complex&)const=0;
    virtual const Complex magnitude(const Complex&)const=0;
    virtual const Complex angle(const Complex&)const=0;
    virtual  const Complex makeFromMagAng
    (const Complex&, const Complex&)const=0;

    const TagType getTag(void)const{return(this->tagString);}
    virtual const Complex tag(const Complex& z)const
    {return(attachTag(this->getTag(),z));}
private:
    const TagType tagString;
};

template <typename FieldType>
class ComplexRectangular :public ComplexProcedure{
public:
    ComplexRectangular(void):ComplexProcedure("rectangular"){}

    const Complex realPart(const Complex& z)const override
    {return(car(z));}
    const Complex imagPart(const Complex& z)const override
    {return(cadr(z));}
   
    const Complex makeFromRealImag
    (const Complex& x, const Complex& y)const override
    {return(makeList(x,y));}
   
    const Complex magnitude(const Complex& z)const override
    {
        return(makeLeaf
               (squareRoot
                (value<FieldType>
                 (square(this->realPart(z))
                  +square(this->imagPart(z))))));
    }
    const Complex angle(const Complex& z)const override
    {
        return(makeLeaf
               (arcTangent2(value<FieldType>(this->imagPart(z)),
                            value<FieldType>(this->realPart(z)))));
    }
   
    const Complex makeFromMagAng
    (const Complex& r, const Complex& a)const override
    {
        return(this->makeFromRealImag
               (makeLeaf(value<FieldType>(makeLeaf(r))*
                         cosine(value<FieldType>(makeLeaf(a)))),
                makeLeaf(value<FieldType>(makeLeaf(r))*
                         sine(value<FieldType>(makeLeaf(a))))));
    }

};


template <typename FieldType>
class ComplexPolar:public ComplexProcedure{
public:
    ComplexPolar(void):ComplexProcedure("polar"){}
   
    const Complex realPart(const Complex& z)const override{
        return(makeLeaf(value<FieldType>(this->magnitude(z))*
                        cosine(value<FieldType>(this->angle(z)))));
    }
    const Complex imagPart(const Complex& z)const override{
        return(makeLeaf(value<FieldType>(this->magnitude(z))*
                        sine(value<FieldType>(this->angle(z)))));
    }
   
    const Complex makeFromRealImag
    (const Complex& x, const Complex& y)const override
    {
        return(this->makeFromMagAng
               (makeLeaf
                (squareRoot(value<FieldType>(square(x)+square(y)))),
                makeLeaf(arcTangent2(value<FieldType>(y),
                                     value<FieldType>(x)))));
    }
   
    const Complex magnitude(const Complex& z)const override
    {return(car(z));}
    const Complex angle(const Complex& z)const
    {return(cadr(z));}
   
    const Complex makeFromMagAng
    (const Complex& r, const Complex& a)const override
    {return(makeList(r,a));}
};


void activateComplexPackage(ComplexProcedure* _complex)
{
    put("real-part",_complex->getTag(),
        makeLeaf(function<Complex(Complex)>
                 ([_complex](const Complex& z)
                  {return(_complex->realPart(z));})));
   
    put("imag-part",_complex->getTag(),
        makeLeaf(function<Complex(Complex)>
                 ([_complex](const Complex& z)
                  {return(_complex->imagPart(z));})));
   
    put("magnitude",_complex->getTag(),
        makeLeaf(function<Complex(Complex)>
                 ([_complex](const Complex& z)
                  {return(_complex->magnitude(z));})));
   
    put("angle",_complex->getTag(),
        makeLeaf(function<Complex(Complex)>
                 ([_complex](const Complex& z)
                  {return(_complex->angle(z));})));
   
    put("make-from-real-imag",_complex->getTag(),
        makeLeaf(function<Complex(Complex,Complex)>
                 ([_complex](const Complex x, const Complex y)
                  {return(_complex->tag
                          (_complex->makeFromRealImag(x,y)));})));
   
    put("make-from-mag-ang",_complex->getTag(),
        makeLeaf(function<Complex(Complex,Complex)>
                 ([_complex](const Complex r, const Complex a)
                  {return(_complex->tag
                          (_complex->makeFromMagAng(r,a)));})));
}

//---------abstraction barrier---------
const Complex realPart(const Complex& z)
{return(applyGeneric("real-part",z));}

const Complex imagPart(const Complex& z)
{return(applyGeneric("imag-part",z));}

const Complex magnitude(const Complex& z)
{return(applyGeneric("magnitude",z));}

const Complex angle(const Complex& z)
{return(applyGeneric("angle",z));}

template<typename ReType,typename ImType>
const Complex makeFromRealImag(const ReType& x,const ImType& y)
{
    return(executable<Complex,Complex,Complex>
           (get("make-from-real-imag","rectangular"))
           (makeLeaf(x),makeLeaf(y)));
}

template<typename MagType,typename AngType>
const Complex makeFromMagAng(const MagType& r,const AngType& a)
{
    return(executable<Complex,Complex,Complex>
           (get("make-from-mag-ang","polar"))
           (makeLeaf(r),makeLeaf(a)));
}

//---------abstraction barrier---------

typedef double Field;

ComplexProcedure* _complexPackage1(nullptr);
ComplexProcedure* _complexPackage2(nullptr);

const void installRectangularComplexPackage(void){
    _complexPackage1=new ComplexRectangular<Field>();
    activateComplexPackage(_complexPackage1);
}
const void installPolarComplexPackage(void){
    _complexPackage2=new ComplexPolar<Field>();
    activateComplexPackage(_complexPackage2);
}

const void uninstallComplexPackages(void){
    if(nullptr!=_complexPackage1) delete _complexPackage1;
    if(nullptr!=_complexPackage2) delete _complexPackage2;
}

int main(int argc, char** argv)
{
    installRectangularComplexPackage();
    installPolarComplexPackage();
   

    const Complex c1(makeFromRealImag(1.0,2.0));
    cout<<"c1 = "<<listString(c1)<<endl;
    cout<<"(real-part c1) = "<<listString(realPart(c1))<<endl;
    cout<<"(magnitude c1) = "<<listString(magnitude(c1))<<endl;
    cout<<endl;

    const Complex c2(makeFromMagAng(1.0,M_PI/4.0));
    cout<<"c2 = "<<listString(c2)<<endl;
    cout<<"(real-part c2) = "<<listString(realPart(c2))<<endl;
    cout<<"(magnitude c2) = "<<listString(magnitude(c2))<<endl;

    uninstallComplexPackages();
    return(0);
}
----
出力
----
c1 = ('rectangular 1 2)
(real-part c1) = 1
(magnitude c1) = 2.23606797749979

c2 = ('polar 1 0.7853981633974483)
(real-part c2) = 0.7071067811865476
(magnitude c2) = 1


0 件のコメント :

コメントを投稿