2012-09-11

SCIP in C++11 ― 2.5.1節


汎用算術演算パッケージと問題2.772.80

データ主導プログラミングは、C++でいう、クラスによって型ごとの実装を決め、
テンプレートで振り分ける、という発想に近いとは思う。
この本では型タグで振り分けを行なっているけれど、
要はその振り分けを継承でもテンプレートでもなんでも使って行えればいいのだから、
C++では普通に動的/静的ポリモーフィズムでもできること。

ただ、C++でここまで一般的な汎用インターフェースというのはあまり考えたことはない。
というかC++だと、型による振り分けのインターフェースの調整が、
相当に面倒になるだろうなあ。実際にはそんなことが必要な局面があったら、
型がやかましいC++よりはさっさと、全体のコントロールにPerlとか使って、
パフォーマンスが要求される部分だけ、C++のプログラムを呼び出すとかしてきた。
ただPerlPerlでいろいろ面倒なので、Lispでというのはありうる選択肢かも。

一応クラスを使っているのだが、クラスの存在意義は相当薄い。
ていうかこの実装、ちょっと危ない。
2.22のような演算テーブルに、クロージャを収めているわけだが、
クロージャにキャプっているのは各型のクラスのポインタだけだから、
そのクラスポインタがどこかでdeleteされると、演算テーブルが死ぬ。
かといってクラスオブジェクト自体をキャプるのは、あまりに大仰でメモリ食いだろう。
namespaceか、クラス使うにしてもstatic関数をキャプる方が、実際に使うとなると安全だ。

しかし特に問題2.77、これだけで動いちゃうのか・・・。
C++で実装するとなると、型インターフェースとか考えなくていいものをちゃんと
考えなきゃいけない分、コードの理解も進むので、
動くはずのものだってのは分かるんだけど、
それでもなんか魔法にかけられたような気がする。すげー。

演算へのタグ付けは、2.4節ではあまり深く考えてなかったが、
引数の数に応じてやってたんだね。今理解したわ。
45にそう書いてはあるが、いまいちピンときてなかった。
ComplexProcedureapply-genericで、この辺はSICPと同じくリストに直した(略)。

問題2.792.80が意外に障害に。
apply-genericListしか返せないので、boolを返したいのに、
一度Listに収めないといけない。apply-genericを使わない手もあるが、
そうすると個別に実装しなおし。う~ん、どっちがいいのかなあ。
とりあえずapply-generic版で。

----
const TagType typeTag(const List& datum)
{
    if(isNumber(datum)){return(TagType("number"));}
    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(isNumber(datum)){return(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(makeList(operation,makeList(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 TagType typeTag2(typeTag(arg2));
    const List procedureLeaf
        (get(makeList(operation,makeList(typeTag1,typeTag2))));
    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());
}

// complex rectangular and polar...

//---------abstraction barrier---------
class ComplexArithmetic{
public:
    ComplexArithmetic(void):
        tagString("complex"),
        _complexProcedure1(new ComplexRectangular<Field>()),
        _complexProcedure2(new ComplexPolar<Field>())
    {
        put(makeList("add",makeList(this->getTag(),this->getTag())),
            makeLeaf(function<Complex(Complex,Complex)>
                     ([this](const Complex& x,const Complex& y)
                      {return(this->tag(this->addComplex(x,y)));})));
        put(makeList("sub",makeList(this->getTag(),this->getTag())),
            makeLeaf(function<Complex(Complex,Complex)>
                     ([this](const Complex& x,const Complex& y)
                      {return(this->tag(this->subComplex(x,y)));})));
        put(makeList("mul",makeList(this->getTag(),this->getTag())),
            makeLeaf(function<Complex(Complex,Complex)>
                     ([this](const Complex& x,const Complex& y)
                      {return(this->tag(this->mulComplex(x,y)));})));
        put(makeList("div",makeList(this->getTag(),this->getTag())),
            makeLeaf(function<Complex(Complex,Complex)>
                     ([this](const Complex& x,const Complex& y)
                      {return(this->tag(this->divComplex(x,y)));})));
        put(makeList("real-part",makeList(this->getTag())),
            makeLeaf(function<Complex(Complex)>(realPart)));
        put(makeList("imag-part",makeList(this->getTag())),
            makeLeaf(function<Complex(Complex)>(imagPart)));
        put(makeList("magnitude",makeList(this->getTag())),
            makeLeaf(function<Complex(Complex)>(magnitude)));
        put(makeList("angle",makeList(this->getTag())),
            makeLeaf(function<Complex(Complex)>(angle)));
        put(makeList("equ?",makeList(this->getTag(),this->getTag())),
            makeLeaf(function<List(Complex,Complex)>
                     ([this](const Complex& x,const Complex& y)
                      {return(this->isEquComplex(x,y));})));
        put(makeList("=zero?",makeList(this->getTag())),
            makeLeaf(function<List(Complex)>
                     ([this](const Complex& x)
                      {return(this->isZeroComplex(x));})));
        put(makeList("make-from-real-imag",this->getTag()),
            makeLeaf(function<Complex(Complex,Complex)>
                     ([this](const Complex& x,const Complex& y)
                      {return(this->tag(this->makeFromRealImag(x,y)));})));
        put(makeList("make-from-mag-ang",this->getTag()),
            makeLeaf(function<Complex(Complex,Complex)>
                     ([this](const Complex& x,const Complex& y)
                      {return(this->tag(this->makeFromMagAng(x,y)));})));

    }
   
    virtual ~ComplexArithmetic(void){
        delete _complexProcedure1;
        delete _complexProcedure2;
    };

    const Complex makeFromRealImag
    (const Complex& x,const Complex& y)const
    {
        return(executable<Complex,Complex,Complex>
               (get(makeList("make-from-real-imag","rectangular")))
               (x,y));
    }
   
    const Complex makeFromMagAng
    (const Complex& r,const Complex& a)const
    {
        return(executable<Complex,Complex,Complex>
               (get(makeList("make-from-mag-ang","polar")))
               (r,a));
    }
   
    virtual const Complex addComplex
    (const Complex& x, const Complex& y)const
    {
        return(this->makeFromRealImag(realPart(x)+realPart(y),
                                      imagPart(x)+imagPart(y)));
    }
    virtual const Complex subComplex
    (const Complex& x, const Complex& y)const
    {
        return(this->makeFromRealImag(realPart(x)-realPart(y),
                                      imagPart(x)-imagPart(y)));
    }
    virtual const Complex mulComplex
    (const Complex& x, const Complex& y)const
    {
        return(this->makeFromMagAng(magnitude(x)*magnitude(y),
                                    angle(x)+angle(y)));
    }
    virtual const Complex divComplex
    (const Complex& x, const Complex& y)const
    {
        return(this->makeFromMagAng(magnitude(x)/magnitude(y),
                                    angle(x)-angle(y)));
    }

    virtual const List isEquComplex
    (const Complex& x, const Complex& y)const
    {return(makeLeaf(realPart(x)==realPart(y)
                     && imagPart(x)==imagPart(y)));}
   
    virtual const List isZeroComplex(const Complex& x)const
    {return(makeLeaf(isEqNumber(realPart(x),0)
                     && isEqNumber(imagPart(x),0)));}
   
    const TagType getTag(void)const{return(this->tagString);}
    virtual const Complex tag(const Complex& x)const
    {return(attachTag(this->getTag(),x));}

private:
    const TagType tagString;
    const ComplexProcedure* _complexProcedure1;
    const ComplexProcedure* _complexProcedure2;
};

ComplexArithmetic* _complexPackage(nullptr);

void installComplexPackage(void){
    _complexPackage=new ComplexArithmetic();
}

void uninstallComplexPackage(void){
    if(nullptr!=_complexPackage) delete _complexPackage;
}

template<typename XType, typename YType>
const Complex makeComplexFromRealImag(const XType& x, const YType& y)
{
    return(executable<Complex,Complex,Complex>
           (get(makeList("make-from-real-imag","complex")))
           (makeLeaf(x),makeLeaf(y)));
}

template<typename XType, typename YType>
const Complex makeComplexFromMagAng(const XType& x, const YType& y)
{
    return(executable<Complex,Complex,Complex>
           (get(makeList("make-from-mag-ang","complex")))
           (makeLeaf(x),makeLeaf(y)));
}



//---------abstraction barrier---------
typedef List Number;

class NumberArithmetic{
public:
    NumberArithmetic(void):tagString("number"){
        put(makeList("add",makeList(this->getTag(),this->getTag())),
            makeLeaf(function<Number(Number,Number)>
                     ([this](const Number& x,const Number& y)
                      {return(this->add(x,y));})));
        put(makeList("sub",makeList(this->getTag(),this->getTag())),
            makeLeaf(function<Number(Number,Number)>
                     ([this](const Number& x,const Number& y)
                      {return(this->sub(x,y));})));
        put(makeList("mul",makeList(this->getTag(),this->getTag())),
            makeLeaf(function<Number(Number,Number)>
                     ([this](const Number& x,const Number& y)
                      {return(this->mul(x,y));})));
        put(makeList("div",makeList(this->getTag(),this->getTag())),
            makeLeaf(function<Number(Number,Number)>
                     ([this](const Number& x,const Number& y)
                      {return(this->div(x,y));})));
        put(makeList("equ?",makeList(this->getTag(),this->getTag())),
            makeLeaf(function<List(Number,Number)>
                     ([this](const Number& x,const Number& y)
                      {return(this->isEqu(x,y));})));
        put(makeList("=zero?",makeList(this->getTag())),
            makeLeaf(function<List(Number)>
                     ([this](const Number& x)
                      {return(this->isZero(x));})));
        put(makeList("make",this->getTag()),
            makeLeaf(function<Number(Number)>
                     ([this](const Number& x)
                      {return(x);})));

    }
    virtual ~NumberArithmetic(void){};
   
    virtual const Number add
    (const Number& x, const Number& y)const
    {return(x+y);}
    virtual const Number sub
    (const Number& x, const Number& y)const
    {return(x-y);}
    virtual const Number mul
    (const Number& x, const Number& y)const
    {return(x*y);}
    virtual const Number div
    (const Number& x, const Number& y)const
    {return(x/y);}
    const List isEqu(const Number& x, const Number& y)const
    {return(makeLeaf(x==y));}
    const List isZero(const Number& x)const
    {return(makeLeaf(isEqNumber(x,0)));}


    const TagType getTag(void)const{return(this->tagString);}
    virtual const Number tag(const Number& x)const
    {return(attachTag(this->getTag(),x));}

private:
    const TagType tagString;
};

NumberArithmetic* _numberPackage(nullptr);

void installNumberPackage(void){
    _numberPackage=new NumberArithmetic();
}

void uninstallNumberPackage(void){
    if(nullptr!=_numberPackage) delete _numberPackage;
}

template<typename NumType>
const Number makeNumber(const NumType& x)
{return(executable<Number,Number>
        (get(makeList("make","number")))(makeLeaf(x)));
}


//---------abstraction barrier---------
typedef List Rational;

class RationalArithmetic{
public:
    RationalArithmetic(void):tagString("rational"){
        put(makeList("add",makeList(this->getTag(),this->getTag())),
            makeLeaf(function<Rational(Rational,Rational)>
                     ([this](const Rational& x,const Rational& y)
                      {return(this->tag(this->add(x,y)));})));
        put(makeList("sub",makeList(this->getTag(),this->getTag())),
            makeLeaf(function<Rational(Rational,Rational)>
                     ([this](const Rational& x,const Rational& y)
                      {return(this->tag(this->sub(x,y)));})));
        put(makeList("mul",makeList(this->getTag(),this->getTag())),
            makeLeaf(function<Rational(Rational,Rational)>
                     ([this](const Rational& x,const Rational& y)
                      {return(this->tag(this->mul(x,y)));})));
        put(makeList("div",makeList(this->getTag(),this->getTag())),
            makeLeaf(function<Rational(Rational,Rational)>
                     ([this](const Rational& x,const Rational& y)
                      {return(this->tag(this->div(x,y)));})));
        put(makeList("equ?",makeList(this->getTag(),this->getTag())),
            makeLeaf(function<List(Rational,Rational)>
                     ([this](const Rational& x,const Rational& y)
                      {return(this->isEqu(x,y));})));
        put(makeList("=zero?",makeList(this->getTag())),
            makeLeaf(function<List(Rational)>
                     ([this](const Rational& x)
                      {return(this->isZero(x));})));
        put(makeList("make",this->getTag()),
            makeLeaf(function<Rational(Rational,Rational)>
                     ([this](const Rational& x,const Rational& y)
                      {return(this->tag(this->makeRational(x,y)));})));
       

    }
    virtual ~RationalArithmetic(void){};

    const Rational gcd(const Rational& a, const Rational& b)const{
        if(makeLeaf(0)==b){return(a);}
        return(gcd(b,a%b));
    }

    const Rational makeRational
    (const List& numerator,const List& denominator)const
    {
        const List g(this->gcd
                     (makeLeaf(abs(value<int>(numerator))),
                      makeLeaf(abs(value<int>(denominator)))));
        if(denominator<makeLeaf(0))
            {return(cons(makeLeaf(-1)*numerator/g,
                         makeLeaf(-1)*denominator/g));}
        return(cons(numerator/g,denominator/g));
    }

    const Rational numer(const Rational& x)const{return(car(x));}
    const Rational denom(const Rational& x)const{return(cadr(x));}
   
    virtual const Rational add
    (const Rational& x, const Rational& y)const
    {
        return(this->makeRational(this->numer(x)*this->denom(y)
                                  +this->numer(y)*this->denom(x),
                                  this->denom(x)*this->denom(y)));
    }
    virtual const Rational sub
    (const Rational& x, const Rational& y)const
    {
        return(this->makeRational(this->numer(x)*this->denom(y)
                                  -this->numer(y)*this->denom(x),
                                  this->denom(x)*this->denom(y)));
    }
    virtual const Rational mul
    (const Rational& x, const Rational& y)const
    {
        return(this->makeRational(this->numer(x)*this->numer(y),
                                  this->denom(x)*this->denom(y)));
    }
    virtual const Rational div
    (const Rational& x, const Rational& y)const
    {
        return(this->makeRational(this->numer(x)*this->denom(y),
                                  this->denom(x)*this->numer(y)));
    }
    virtual const List isEqu
    (const Rational& x, const Rational& y)const
    {
        return(makeLeaf(this->numer(x)==this->numer(y)
                        && this->denom(x)==this->denom(y)));
    }
    virtual const List isZero
    (const Rational& x)const
    {return(makeLeaf(isEqNumber(this->numer(x),0)));}



    const TagType getTag(void)const{return(this->tagString);}
    virtual const Rational tag(const Rational& x)const
    {return(attachTag(this->getTag(),x));}

private:
    const TagType tagString;
};

RationalArithmetic* _rationalPackage(nullptr);

void installRationalPackage(void){
    _rationalPackage=new RationalArithmetic();
}

void uninstallRationalPackage(void){
    if(nullptr!=_rationalPackage) delete _rationalPackage;
}

template<typename XType, typename YType>
const Rational makeRational(const XType& x, const YType& y)
{
    return(executable<Rational,Rational,Rational>
           (get(makeList("make","rational")))(makeLeaf(x),makeLeaf(y)));
}






//---------abstraction barrier---------
const List add(const List& x,const List& y)
{
    return(applyGeneric("add",x,y));
}
const List sub(const List& x,const List& y)
{
    return(applyGeneric("sub",x,y));
}
const List mul(const List& x,const List& y)
{
    return(applyGeneric("mul",x,y));
}
const List div(const List& x,const List& y)
{
    return(applyGeneric("div",x,y));
}
const bool isEqu(const List& x,const List& y)
{
    return(makeLeaf(0)!=applyGeneric("equ?",x,y));
}
const bool isZero(const List& x)
{
    return(makeLeaf(0)!=applyGeneric("=zero?",x));
}


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


int main(int argc, char** argv)
{
    installNumberPackage();
    installRationalPackage();
    installComplexPackage();
   
    const Rational r1(makeRational(3,2));
    const Rational r2(makeRational(1,4));
    cout<<"r1 = "<<listString(r1)<<endl;
    cout<<"r2 = "<<listString(r2)<<endl;
    cout<<"(add r1 r2) = "<<listString(add(r1,r2))<<endl;
    cout<<"(sub r1 r2) = "<<listString(sub(r1,r2))<<endl;
    cout<<"(mul r1 r2) = "<<listString(mul(r1,r2))<<endl;
    cout<<"(div r1 r2) = "<<listString(div(r1,r2))<<endl;
   

    cout<<endl;
    const Complex c1(makeComplexFromRealImag(1.0,2.0));
    const Complex c2(makeComplexFromRealImag(3.0,4.0));
    cout<<"c1 = "<<listString(c1)<<endl;
    cout<<"c2 = "<<listString(c2)<<endl;
    cout<<"(add c1 c2) = "<<listString(add(c1,c2))<<endl;
    cout<<"(sub c1 c2) = "<<listString(sub(c1,c2))<<endl;
    cout<<"(mul c1 c2) = "<<listString(mul(c1,c2))<<endl;
    cout<<"(div c1 c2) = "<<listString(div(c1,c2))<<endl;


    cout<<endl<<"Excersize 2.77:"<<endl;
    cout<<"(real-part c1) = "<<listString(realPart(c1))<<endl;
    cout<<"(magnitude c1) = "<<listString(magnitude(c1))<<endl;
    cout<<endl;

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

    cout<<endl<<"Excersize 2.78:"<<endl;
    const Number n1(makeNumber(3));
    const Number n2(makeNumber(4));
    cout<<"n1 = "<<listString(n1)<<endl;
    cout<<"n2 = "<<listString(n2)<<endl;
    cout<<"(add n1 n2) = "<<listString(add(n1,n2))<<endl;
    cout<<"(sub n1 n2) = "<<listString(sub(n1,n2))<<endl;
    cout<<"(mul n1 n2) = "<<listString(mul(n1,n2))<<endl;
    cout<<"(div n1 n2) = "<<listString(div(n1,n2))<<endl;

    cout<<endl<<"Excersize 2.79:"<<endl;
    const Number n3(makeNumber(3));
    cout<<"n1 = "<<listString(n1)<<endl;
    cout<<"n2 = "<<listString(n2)<<endl;
    cout<<"(equ? n1 n2) = "<<isEqu(n1,n2)<<endl;
    cout<<"n3 = "<<listString(n3)<<endl;
    cout<<"(equ? n1 n3) = "<<isEqu(n1,n3)<<endl;

    cout<<endl;
    const Rational r3(makeRational(3,2));
    cout<<"r1 = "<<listString(r1)<<endl;
    cout<<"r2 = "<<listString(r2)<<endl;
    cout<<"(equ? r1 r2) = "<<isEqu(r1,r2)<<endl;
    cout<<"r3 = "<<listString(r3)<<endl;
    cout<<"(equ? r1 r3) = "<<isEqu(r1,r3)<<endl;
   
    cout<<endl;
    const Complex c4(makeComplexFromRealImag(1,2));
    cout<<"c1 = "<<listString(c1)<<endl;
    cout<<"c2 = "<<listString(c2)<<endl;
    cout<<"(equ? c1 c2) = "<<isEqu(c1,c2)<<endl;
    cout<<"c4 = "<<listString(c4)<<endl;
    cout<<"(equ? c1 c4) = "<<isEqu(c1,c4)<<endl;
   
    cout<<endl<<"Excersize 2.80:"<<endl;
    const Number n4(makeNumber(0));
    cout<<"n1 = "<<listString(n1)<<endl;
    cout<<"(=zero? n1) = "<<isZero(n1)<<endl;
    cout<<"n4 = "<<listString(n4)<<endl;
    cout<<"(=zero? n4) = "<<isZero(n4)<<endl;

    cout<<endl;
    const Rational r4(makeRational(0,2));
    cout<<"r1 = "<<listString(r1)<<endl;
    cout<<"(=zero? r1) = "<<isZero(r1)<<endl;
    cout<<"r4 = "<<listString(r4)<<endl;
    cout<<"(=zero? r4) = "<<isZero(r4)<<endl;
   
    cout<<endl;
    const Complex c5(makeComplexFromRealImag(0,0));
    cout<<"c1 = "<<listString(c1)<<endl;
    cout<<"(=zero? c1) = "<<isZero(c1)<<endl;
    cout<<"c5 = "<<listString(c5)<<endl;
    cout<<"(=zero? c5) = "<<isZero(c5)<<endl;
   
    uninstallNumberPackage();
    uninstallRationalPackage();
    uninstallComplexPackage();
    return(0);
}
----
出力
----
r1 = ('rational 3 2)
r2 = ('rational 1 4)
(add r1 r2) = ('rational 7 4)
(sub r1 r2) = ('rational 5 4)
(mul r1 r2) = ('rational 3 8)
(div r1 r2) = ('rational 6 1)

c1 = ('complex 'rectangular 1 2)
c2 = ('complex 'rectangular 3 4)
(add c1 c2) = ('complex 'rectangular 4 6)
(sub c1 c2) = ('complex 'rectangular -2 -2)
(mul c1 c2) = ('complex 'polar 11.18033988749895 2.034443935795702)
(div c1 c2) = ('complex 'polar 0.447213595499958 0.1798534997924778)

Excersize 2.77:
(real-part c1) = 1
(magnitude c1) = 2.23606797749979

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

Excersize 2.78:
n1 = 3
n2 = 4
(add n1 n2) = 7
(sub n1 n2) = -1
(mul n1 n2) = 12
(div n1 n2) = 0.75

Excersize 2.79:
n1 = 3
n2 = 4
(equ? n1 n2) = 0
n3 = 3
(equ? n1 n3) = 1

r1 = ('rational 3 2)
r2 = ('rational 1 4)
(equ? r1 r2) = 0
r3 = ('rational 3 2)
(equ? r1 r3) = 1

c1 = ('complex 'rectangular 1 2)
c2 = ('complex 'rectangular 3 4)
(equ? c1 c2) = 0
c4 = ('complex 'rectangular 1 2)
(equ? c1 c4) = 1

Excersize 2.80:
n1 = 3
(=zero? n1) = 0
n4 = 0
(=zero? n4) = 1

r1 = ('rational 3 2)
(=zero? r1) = 0
r4 = ('rational 0 1)
(=zero? r4) = 1

c1 = ('complex 'rectangular 1 2)
(=zero? c1) = 0
c5 = ('complex 'rectangular 0 0)
(=zero? c5) = 1

0 件のコメント :

コメントを投稿