汎用算術演算パッケージと問題2.77~2.80
データ主導プログラミングは、C++でいう、クラスによって型ごとの実装を決め、
テンプレートで振り分ける、という発想に近いとは思う。
この本では型タグで振り分けを行なっているけれど、
要はその振り分けを継承でもテンプレートでもなんでも使って行えればいいのだから、
C++では普通に動的/静的ポリモーフィズムでもできること。
ただ、C++でここまで一般的な汎用インターフェースというのはあまり考えたことはない。
というかC++だと、型による振り分けのインターフェースの調整が、
相当に面倒になるだろうなあ。実際にはそんなことが必要な局面があったら、
型がやかましいC++よりはさっさと、全体のコントロールにPerlとか使って、
パフォーマンスが要求される部分だけ、C++のプログラムを呼び出すとかしてきた。
ただPerlはPerlでいろいろ面倒なので、Lispでというのはありうる選択肢かも。
一応クラスを使っているのだが、クラスの存在意義は相当薄い。
ていうかこの実装、ちょっと危ない。
図2.22のような演算テーブルに、クロージャを収めているわけだが、
クロージャにキャプっているのは各型のクラスのポインタだけだから、
そのクラスポインタがどこかでdeleteされると、演算テーブルが死ぬ。
かといってクラスオブジェクト自体をキャプるのは、あまりに大仰でメモリ食いだろう。
namespaceか、クラス使うにしてもstatic関数をキャプる方が、実際に使うとなると安全だ。
しかし特に問題2.77、これだけで動いちゃうのか・・・。
C++で実装するとなると、型インターフェースとか考えなくていいものをちゃんと
考えなきゃいけない分、コードの理解も進むので、
動くはずのものだってのは分かるんだけど、
それでもなんか魔法にかけられたような気がする。すげー。
演算へのタグ付けは、2.4節ではあまり深く考えてなかったが、
引数の数に応じてやってたんだね。今理解したわ。
注45にそう書いてはあるが、いまいちピンときてなかった。
ComplexProcedureやapply-genericで、この辺はSICPと同じくリストに直した(略)。
問題2.79と2.80が意外に障害に。
apply-genericはListしか返せないので、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 件のコメント :
コメントを投稿