2012-08-31

SCIP in C++11 ― 2章書き直し!

2章書きなおし!

リストの内部表現として、STLlistC++11tupleを、
なるべくプリミティブに使って、
STLの機能をできる限り活用しようとしてきたが、
2.3.2節の実装にかかり限界が来たらしい。

柔軟なメソッドやalgorithmが多様に使えるlistや、
静的リスト限定ながら型の自由度が大きいtupleは、
それぞれに魅力的だったのだが、listは型の自由度が小さく、tupleは動的生成に弱い。
で、2.3.2節で構文解析が始まり、多様な型のデータを格納した複雑な木を、
動的に生成しなければならない状況に入って、お手上げ状態に。

2.3.2節だけならまだしも、構文解析はこの本の主要部なだけに、
ここでしっかり実装できなければならない。
う~ん、listtupleを使うのはここまでか。
特にtupleは散々苦労しただけに、ここでやめるのも惜しいが、
LINK
オブジェクト指向における
再利用のための
デザインパターン
(GoF)
苦労した分わかったことも多くて、ここまででも十分財産になった。

2.1.3で痛感させられたように、適切な抽象の壁が設定されている限り、
内部表現はなんでもいいのだ。
動的な木の生成か~、なんて言ったっけこういうの、
大昔勉強したGoFをもう一度見直す。あーそうだ、Compositeパターン
使う必要に迫られたことはなかったから、発想になかったわ。
Flyweightパターンも考慮する必要があるかもだが、
とりあえずこれで行こう。
boost graph libraryとかまで言い出すと大仰すぎるしな。
関数型プログラミングとSTLにこだわるあまり、
C++のクラスを作る事自体を、拒絶してたところもあった。
つくづく自らの硬直した発想力が嫌になる。

ただCompositeパターンは、複雑な木の生成・破棄に関してのメモリマネジメントが、
メモリリークを防ぐためにとても重要。これは手じゃやりきれんので、
今まであまり使ったことないスマートポインタが必須だなこりゃ。
STLauto_ptrとかを元に、参照カウントとか実装しないといけないのかなあ、
と一時途方に暮れたが、よく見ればC++11にはshared_ptrがあるわけで、
これは参照カウントあるじゃん。auto_ptrはむしろdeprecatedなのか。

これで一気に楽になった~、shared_ptrすげー!もう普通のポインタに戻れないかもw
完全なガベージコレクションとはいかないまでも、
自動ムーブコンストラクションとかも多分効いてるだろうから、
多少のマネジメントミスはかなり吸収してくれてるんじゃないかこれ。
shared_ptrを安全に使いこなせてる自信はまだないが、
悩みがだいぶ減ったのは確かだわ。C++11の表現力すげー、感動。
てか本当にすごいのはboostで、boost使いは前から知ってたんだろうが、
自らの不勉強に恥じ入ることしきり。

で、2章のコードはほとんど全部書きなおし。
リスト成分の内部表現はとりあえずstringで、
値の取り出しには別のテンプレートラッパvalue関数を実装する。
まー効率は度外視。これで行けるところまで行ってみよう。
でも基本機能を抽象の壁のむこうに追いやると、思ったより修正箇所が少ないし、
コードもコンパクトでわかりやすくなった。抽象の壁恐るべし。
もっと早くCompositeパターンに気づくべきだった。

基本機能のコードは以下の通り。これで2.2節までうまく行った(2.2.4節除く)。
これを使って、今までの2章のブログ記事もすべて書き直し。
だってヘタクソコードをいつまでも晒すのは恥ずかしいんだもん。

-----
//-------representation of list (Composite pattern) ----------
typedef string LeafItemType;
const LeafItemType nullLeafItem(LeafItemType(""));

class ListTree
{
public:
    virtual ~ListTree(void){}
    const shared_ptr<ListTree> getParentList(void) const{
        return(this->_predecessor);}
    virtual const LeafItemType getItem(void)const{
        return(this->item);}
    virtual const bool isList(void)const{
        return(false);}
    virtual void addElement(const shared_ptr<ListTree>&){
        cerr<<"You cannot add a leaf to a leaf."<<endl;
        exit(1);
    }
    virtual const shared_ptr<ListTree> carElement() const{
        cerr<<"You cannot take the car of a leaf."<<endl;
        exit(1);
    }
    virtual void removeFirstElement(){
        cerr<<"You cannot remove the first element of a leaf."<<endl;
        exit(1);
    }
    virtual const int length(void)const{
        cerr<<"You cannot get the length of a leaf."<<endl;
        exit(1);
    }
    virtual const bool isNull(void)const{return(false);}
    void changePredecessor(const shared_ptr<ListTree>& _predecessorNew){
        this->_predecessor=_predecessorNew;}
    virtual const string getExpression(void) const{
        return(this->getItem());}
    virtual const int getInt(void)const{
        return(stoi(this->getItem()));}
    virtual const double getDouble(void)const{
        return(stod(this->getItem()));}
protected:
    shared_ptr<ListTree> _me;
    ListTree(void)=delete;
    ListTree(shared_ptr<ListTree> _predecessorIn,
             const LeafItemType& itemIn=nullLeafItem):
        _me(nullptr),
        _predecessor(_predecessorIn),
        item(itemIn)
    {
        _me=make_shared<ListTree>(*this);
    }
    
private:
    shared_ptr<ListTree> _predecessor;
    LeafItemType item;
};

class CompositeList:public ListTree
{
public:
    CompositeList(const shared_ptr<ListTree>& _predecessorIn=nullptr):
        ListTree(_predecessorIn),_listTrees(0){}
    virtual ~CompositeList(void){
        if(! this->_listTrees.empty()){
            for_each(this->_listTrees.begin(),this->_listTrees.end(),
                     [](shared_ptr<ListTree> _element){
                         if(_element && _element.unique()){
                             delete _element.get();
                         }
                     });
        }
    }
    virtual const bool isList(void)const{return(true);}
    virtual void addElement(const shared_ptr<ListTree>& _element)
    {
        this->_listTrees.push_front(_element);
        _element->changePredecessor(this->_me);
    }
    virtual const shared_ptr<ListTree> carElement() const{
        return(this->_listTrees.front());
    }
    virtual void removeFirstElement(){this->_listTrees.pop_front();}
    virtual const int length(void)const{return(this->_listTrees.size());}
    virtual const bool isNull(void)const{return(this->_listTrees.empty());}
    virtual const string getExpression(void)const{
        string returnString("(");
        for_each(this->_listTrees.begin(),this->_listTrees.end(),
                 [&returnString](const shared_ptr<ListTree> _element){
                     returnString+=_element->getExpression()+" ";
                 });
        if(!this->isNull()){returnString.pop_back();}
        returnString.push_back(')');
        return(returnString);
    }
    virtual const int getInt(void)const{
        cerr<<"requiring an integer value for a list."<<endl;
        return(numeric_limits<int>::quiet_NaN());}
    virtual const double getDouble(void)const{
        cerr<<"requiring a double value for a list."<<endl;
        return(numeric_limits<double>::quiet_NaN());}
private:
    list<shared_ptr<ListTree>> _listTrees;
};

class Leaf:public ListTree
{
public:
    Leaf(void)=delete;
    Leaf(const LeafItemType& itemIn):ListTree(nullptr,itemIn){}
    virtual ~Leaf(void){}
};
   


   
//---------abstraction barrier---------
typedef shared_ptr<ListTree> List;

// print list
const string listString(const List& _listTree)
{return(_listTree->getExpression());}

// get value expression
//for string
template <typename ReturnType>
const ReturnType value(const List& _listTree)
{return(_listTree->getItem());}
//for list
template<>
const List value<List>(const List& _listTree)
{return(_listTree);}
//for int
template<>
const int value<int>(const List& _listTree)
{return(_listTree->getInt());}
//for double
template<>
const double value<double>(const List& _listTree)
{return(_listTree->getDouble());}


const List makeCopyList(const List& _listTree)
{
    return(make_shared<CompositeList>
           (*dynamic_pointer_cast<CompositeList>(_listTree)));
}

template<typename ItemType>
const List makeLeaf(const ItemType& x)
{
    ostringstream tmpSStreamX;
    tmpSStreamX<<setprecision(16)<<x;
    return(make_shared<Leaf>(tmpSStreamX.str()));
}

template <typename ReturnType,typename ArgType>
const List leafMap(const function<ReturnType(ArgType)> procedure,
                   const List _leaf)
{
    return(makeLeaf(procedure(value<ArgType>(_leaf))));
}

const List cons(void){return(make_shared<CompositeList>());}

template <typename ItemTypeX>
const List cons(const ItemTypeX& x, const List& _yList)
{
    List _yListCopy(makeCopyList(_yList));
    _yListCopy->addElement(makeLeaf(x));
    return(_yListCopy);
}
template<>
const List cons<List>(const List& _xList,const List& _yList){
    List _xListCopy(_xList);
    List _yListCopy(makeCopyList(_yList));
    _yListCopy->addElement(_xListCopy);
    return(_yListCopy);
}

template <typename ItemTypeX>
const List cons(const ItemTypeX& x)
{return(cons(x,cons()));}

template <typename ItemTypeX,typename ItemTypeY>
const List cons(const ItemTypeX& x,const ItemTypeY& y)
{return(cons(x,cons(y)));}

const List car(const List& _listTree)
{
    return(_listTree->carElement());
}

const List cdr(const List& _listTree)
{
    if(!_listTree->isList()){return(cons());}
    List _cdrListTree(makeCopyList(_listTree));
    _cdrListTree->removeFirstElement();
    return(_cdrListTree);
}

const List makeList(void)
{
    return(cons());
}
template<typename ListType1, typename ... ListTypes>
const List makeList(const ListType1& element1, ListTypes... elements)
{
    return(cons(element1,makeList(elements...)));
}

// size of a list
const int length(const List& _listTree)
{
    return(_listTree->length());
}

// null?
const bool isNull(const List& _listTree)
{
    return(_listTree->isNull());
}

// pair?
const bool isPair(const List& _listTree)
{
    return(_listTree->isList());
}


//---------abstraction barrier---------
const List cadr(const List& listIn)
{
    return(car(cdr(listIn)));
}

const List caddr(const List& listIn)
{
    return(car(cdr(cdr(listIn))));
}

2012-08-23

野尻抱影著作データ:やぎ座


  • 「日本の星」(中公文庫BIBLIO) p135~136

野尻抱影著作データ:みなみのうお座


  • 「星三百六十五夜・秋」(中公文庫BIBLIO) p62~63
  • 「星三百六十五夜・秋」(中公文庫BIBLIO) p77~79
  • 「日本の星」(中公文庫BIBLIO) p136~139

野尻抱影著作データ:木星


  • 「続・星と伝説」(中公文庫BIBLIO) p102

野尻抱影著作データ:太陽・月・惑星

野尻抱影著作データ:火星


  • 「続・星と伝説」(中公文庫BIBLIO) p99~p102

野尻抱影著作データ:秋の星

野尻抱影著作データ:みずがめ座


  • 「続・星と伝説」(中公文庫BIBLIO) p125~p131
  • 「星三百六十五夜・秋」(中公文庫BIBLIO) p59~p60
  • 「日本の星」 (中公文庫BIBLIO) p136

2012-08-21

計算の書き順

近所の中1に数学を教えている。
夏休み前に学校では、一次方程式の途中で終わったようだが、
生徒は一次方程式の、というか式の計算にまだ慣れないので、
夏休みに間を空けすぎずに、コツコツと練習を積むことが大事な時期だ。

中1の2学期に入るまでに、一次方程式の計算に、
ある程度自信が持てるようになるか、それとも苦手意識を抱いてしまうかで、
その後の中学・高校での数学の学習意欲が大きく変わってくる。
地味だが、一生の進路を左右するほどの大切さがある部分だ。

で、見ている中1の子は、一次方程式を解く作業自体は、
式の置き換えゲームの感覚で楽しめている。
それに小学校レベルの正の数の計算自体は、自信を持っているし速い。
ところが2問に1箇所くらいは間違えて、答えが合わない場合が多い。
間違えるパターンはほぼ決まっている:
  • 正の整数と負の整数の加法や乗法で、絶対値は合っているが符号を間違える。
  • 分母を払う時、左辺だけに因子をかけて右辺にかけ忘れたり、因子を一つの項に分配し忘れたりする。
  • 自分の約分計算の結果を見間違える。
上の間違いに自分で気づくこともあるが、
とにかく消しゴムを使う局面が多い。
さらによく見ていると、どうも書き方や書き順が悪い。

書き方は数学において重要な要素だが、
それでも書き順なんて細かいことを口うるさく言いたくはない。
しかし、書いている順番はすなわち考えている順番だ。
考えている順番とはすなわち論理だ。
論理とはすなわち数学そのものだ。

だから書く順番の型稽古は中1にとって必要なことだなと思い、
事細かく口出しして修正していたら、生徒はうるさいなぁと感じたみたいだが、
結果として楽に、間違いを少なく計算できることには気づいてくれたようで、
最後は複雑な方程式の計算もこなせるようになってくれた。
うるさいなぁと思いながらも言うことを聞いてくれるのは、
生徒本人の素直さと、生徒との普段の信頼関係が生きたのかなと思う。

あとは、夏休みの残りで忘れてしまわないことを祈る・・・。

  • 符号の間違い

例えば4-6を計算するときに結果の絶対値である2を書いてから、
-符号をつけて-2にする。
これは頭の中で数直線のイメージ、
つまり一次元のベクトル演算の考えに当たるものが、
なんとなくあるんだろうと思う、てかそう教えたし。
で、+方向に4行ってから、-方向に6行って、
0から-方向に2飛び出るから、まず2を書き、
次に向きが0から-方向なので、-符号をつける。

その考え自体は間違ってない、というより本道だ。
「4-6を計算せよ」と言えばその子も出来る。
が、文字式・方程式の計算の中で、
大量の正負の数の演算を次々にやる局面では、
一つの演算の絶対値が出たところまでで安心してしまって、
-符号を付け忘れるというハメになりがちだ。

原理としては一次元ベクトル演算として理解する必要があるのだが、
それはそれとして計算技術としては、
まず正と負のどちらが強いのかみて、
符号を一番最初に書くべきだ。
その次に、強い方の絶対値から弱い方の絶対値を引き算して絶対値を書く。
4-6=-6+4=-(6-4)という変形は、まだ慣れていなくてピンとこないから、
計算技術の型としてまず慣れさせるのがいいのだろう。
乗法もまず符号を書いてから、絶対値の演算をさせる。

正負の数を教えた時に、
数直線での一次元ベクトル計算としての面だけを強調しすぎたな・・・。
そういえば自分も中1の時に、理屈がわからなくて違和感を感じながらも、
とりあえず上の書き順の計算法で、楽に計算できることは納得できた。
こういう技術的な面も、正負の数をやる最初の段階で、
もっと型稽古させるべきだったと反省。
今ならまだ、変なクセを直すのは間に合うから、直させないと。
生徒が数を先に書く度に「まず符号!」と口酸っぱく言ったが、
これからも言い続けないといけないと思う。


  • 分母を払う間違い

有理数係数の一次方程式は分母を払って、
整数係数にして楽に計算することを学ぶ。
問題はどの段階でどのように分母を払うか、
分母を払うときに書き方をどうするか。

例えば

(x-3)/2-(x-2)/3=4

なんていう一次方程式。
慣れていれば即、両辺に6を掛けて、

3(x-3)-2(x-2)=24

とするところだが、まだ項の概念に慣れないので、
左辺にかけた6をどのように分配するのか、生徒は迷う。
慣れないうちはまず、確実に出来る方法だな。
ある程度慣れたら、スピードアップのために、
上でいいことを教えたいところだが、最初はそうしない。

(x-3)/2や-(x-2)/3は見づらい。
とくに後者は、隠れた係数-1と1/3が別々の場所に分離しているから、
なお見づらい。だからまず

1/2・(x-3)-1/3・(x-2)=4

と書きなおさせ、さらに先に括弧を展開させて

x/2-3/2-x/3+2/3=4

とする。それから分母を払うが、
ここで6を全体にかけている形を明示的に書かせる。つまり

6(x/2-3/2-x/3+2/3)=6×4

をきちんと書く。これで両辺へのかけ忘れが割と減る。さらに

6×x/2-6×3/2-6×x/3+6×2/3=24

も明示的に書かせる。これで左辺各項への6の分配漏れがほぼ無くなる。

とにかく最初に優先すべきなのは正確さで、スピードはその次。
そのために、1ステップ1ステップは単純で確実なことしかやらない。


  • 約分の見間違え

6×x/2-6×3/2-6×x/3+6×2/3=24

まで来た方程式で、次に各項の約分をするのだが、
生徒は

63×x/2 -63×3/2 -62×x/3 +62×2/3=24

と約分を先に全部書いてしまうクセがついている模様。
そもそも約分で63みたいに線で数字を消してしまうのは、
式がぐちゃぐちゃになって見づらくなるし、あとで修正が効かないので、
好きではないのだが、まあこれは許容することにする。
それでも、例えば最初に63×x/2 を書いたあと、そこから目を離してしまうと、
次の式を書くために戻ってきた時に、見間違えが非常に多くなる。
さらに63のように約分の結果としての3は小さく書くことが多いので、
なおさら見間違えが多くなる。

別に綺麗な字で書けとは言わない。
むしろ綺麗さにこだわる子は大抵、見た目の綺麗さ・整え方に神経が行って、
却って集中を欠いたりする。
だが区別できる字で書かなければならない。
具体的には、ノートの罫の1行に1式を書いていると、
有理式ではスペースが足りなさすぎて、
区別しづらい小さな字で書かざるをえない。
そこでまずは、式を全て2行に1式にして大きく書くよう指導。

それから、

6×x/2-6×3/2-6×x/3+6×2/3=24

の各項の約分を計算するときに、

63×x/2 -6×3/2-6×x/3+6×2/3=24

のように最初の項の約分を書いたら、すぐに次の行に結果の

3x

を書くよう指導。項ごとに、約分をやったらその結果をすぐに次の行に書くことで、
今やったばかりの約分から目を離させない。
これで約分の間違いはだいぶ減った。


  • その他気づいたこと


生徒には、上のような間違いに加え、方程式を解いていく時に、
ある行を書いたあと、次の行を書き始める時に、
前の行と同じ位の位置に=をまず書いてしまうクセがあった。

方程式でなく式の計算では、次の行に行く時に行頭の=をまず書くから、
そのノリで=を最初に行中に書いてしまうのだが、
結局左辺を書くスペースが足りなくなって、
書いた=を消しゴムで消すハメになる局面が多くなる。
それで左辺の途中で思考が途切れ、消し終わってからの再開時に間違える。

消しゴムはなるべく使わないよう指導している。
間違えに気付き、鉛筆を横に置き、消しゴムを手に取り、
消しゴムで狙った場所を綺麗に消して、
消しカスを払い、消しゴムを置いて、鉛筆を手に取る、
という一連のアクションで、思考は完全に途切れてしまう。

そんなことをやっている暇があったら、一行まるごと

6×x/2-6×3/2-6×x/3+6×2/3=24

と一本引いてなしにして、思考を途切れさせず次に行くべきだ。
まあ一文字の明らかな書き間違えなんかの時には消しゴムを許容しているが、
一行~数行に渡る式をまるごと消しゴムで消そうとするのは、絶対に禁じている。

本当はボールペンでノートを書かせたい。
が、実際にやるとなると、「消せない」という事に神経が割かれて、
却って集中を欠きはしないかと思い、ためらっている。

ともかく、口を酸っぱく、方程式では最初に=を書くなと指導。
これが、本人も分かっちゃいるのだがなかなか直らない。
クセというのは恐ろしい。

これからしばらく、もうちょっと口うるさくなることにしよう。

2012-08-13

SCIP in C++11 ― 2.3.1節


クォートと問題2.54

問題2.532.55Scheme特有の話なのでパス。

Schemeでのeq?はアドレスの比較で、equal?は値の比較なのだが、
実際の使われ方を見ているとC++での対応物として、
さしあたりeq?List内部のstringの値の比較として差し支えないように見える。
が、3章で参照が出た時に困ってくるのかも・・・。
アドレスの比較でちゃんと実装しようと思ったら、
Flyweightパターン使ってリスト管理をしないといけないと思うが、
そこまでする必要があるのか現時点で確信できない。
とりあえず値の比較で押してみるか。

----
//eq?
template <typename LeafType>
const bool isEq(const LeafType& leafItem,
                const List& x)
{
    return(leafItem==value<LeafType>(x));
}


//equal?
template<typename LeafType>
const bool isEqual(const List& list1,
                   const List& list2)
{
    if(isNull(list1) && isNull(list2)){return(true);}
    else if(isNull(list1) || isNull(list2)){return(false);}
    else if(value<LeafType>(car(list1))
            !=value<LeafType>(car(list2)))
        {return(false);}
    return(isEqual<LeafType>(cdr(list1),cdr(list2)));
}

//memq
template <typename LeafType>
const List memq(const LeafType& searchLeafItem,
                const List& x)
{
    if(isNull(x)){return(x);}
    else if(isEq(searchLeafItem,car(x))){return(x);}
    return(memq(searchLeafItem,cdr(x)));
}




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

int main(int argc, char** argv)
{
    const auto list1(makeList("apple","banana","prune"));
    cout<<"list1 ="<<listString(list1)<<endl;
    cout<<"(memq 'banana list1) = "
        <<listString(memq(string("banana"),list1))<<endl;

    cout<<endl<<"Excersize 2.54:"<<endl;
    const auto list2(makeList("apple","banana","pear"));
    cout<<"list2 ="<<listString(list2)<<endl;
    cout<<"(equal? list1 list2) = "<<isEqual<string>(list1,list2)<<endl;
   
    cout<<endl;
    const List list3(makeList("apple","banana","prune"));
    cout<<"list3 ="<<listString(list3)<<endl;
    cout<<"(equal? list1 list3) = "<<isEqual<string>(list1,list3)<<endl;
   


    return(0);
}
----
出力
----
list1 =(apple banana prune)
(memq 'banana list1) = (banana prune)

Excersize 2.54:
list2 =(apple banana pear)
(equal? list1 list2) = 0

list3 =(apple banana prune)
(equal? list1 list3) = 1