2012-09-26

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


銀行口座その1:口座の残高管理と局所状態変数

自前のListshared_ptr<ListTree>typedefしている)で代入を可能にするため、
ListTreeクラス内のitemを書き替える関数putItemを実装(略)。
しかしC++11ではクロージャでキャプったオブジェクトはconstになっちゃう。
いままでは変える必要がなかったから気づかなかった。
キャプったListに代入をやるには、
const_castを使わんといかんが、const_castはなんかヤバい香りがビリビリ・・・。

2.4.3節でメッセージパッシングをやったときは、
代入は入らなかったから、なんとなくうまくやれたのだが、
代入が入って、balanceを別々のラムダの間で共有しなければならない。
これがうまくいかない。変数のスコープや参照と実体の区別を注意深ーく見ながら、
といってもその「実体」がまたスマートポインタなのでややこしいが、
とにかくなんとか藻掻く。

まず、どこか別のところ(main関数とか)で、balanceオブジェクトを保持していれば、
make-accountwithdrawとかdepositとかで
キャプったbalanceへの参照も維持できてうまく動く。
が、それはつまるところ、口座を表現するdispatch関数が自分で独立に動けず、
全然関係ないところでいつ消されるかわからないオブジェクトへの、
参照に依存することになって、あまりに危険。

そこで、今まで何度か、うまく行かなくて藻掻く度に試してはいた、
右辺値参照によるムーブコンストラクションを、あまり期待せずに使ってみる
あれ、できちゃった。
moveを噛まさないとなのでちょっと面倒で、
この先moveがないといけないことを忘れずにうまく使えるかちょっと不安になるが・・・。

そしてそれ以上に疑問が山積する。balanceオブジェクトへの参照は、
右辺値参照によってwithdrawdeposit内に持ち込まれたのだが、
元のbalanceオブジェクト自体を保持しているのは誰なんだ?
単純な左辺値参照を使ったときはwithdrawdepositも保持できていなかったので、
withdrawdepositであるという気がしない。
右辺値参照ならwithdrawdepositが保持してくれてるんだろうか?
withdrawdepositが破棄されたらbalanceも破棄してくれるんだろうか?
それとも破棄できないメモリリークを作ってしまったんだろうか?
Schemeでは大丈夫なのはなぜなんだろう?
Schemeで外部の参照をキャプった時に何が起きてるんだろう?

ああ、そういえばSchemeで最初勉強してた時にも、
ここでのSchemeでのスコープの概念がよく分からなくて混乱したまま、
回路シミュレータに入ってわけがわからなくなったんだ。
Schemeで何が起きてるかは、5章でSchemeインタープリタを作るときとかに、
種明かしがあると期待するが、C++11の右辺値参照の方は、
なんかとっても危険なことをしている気がして不安。

ありうるアイデアは、Flyweightパターンを入れて、
Listオブジェクト全部を一括管理してガベージコレクションすることだが・・・
とりあえずそこまでしないでおいて進んでみよう・・・。

LINK
Paul Graham
(川合史朗監訳)
ハッカーと画家
最近Paul Grahamのエッセイを色々見ているが興味深い。
特に印象的だったのは「技術野郎の復讐」の付録にある(訳:川合史朗氏)  、

「あなたが難しい問題を解こうとしているなら、
問われているのはパワフルな言語を使うか使わないか、ではない。
(a)パワフルな言語を使うか
(b)パワフルな言語と等価なインタプリタを書くか
(c)自らがパワフルな言語の人間コンパイラとなるか、
という選択なのだ。
(中略)コンパイラがやるべきことを人間がシミュレートするという慣行は
ただ広まっているというだけでなく、思考を型にはめる作用がある。
 例えば、OOの世界では非常に良く「パターン」というのを耳にするだろう。
 この「パターン」は多くの場合、(c)のケース、
 すなわち人間コンパイラが実際に動作している証拠なんじゃないかと私は思う。」

いま自分がC++11で書いていて、上のような疑問をいろいろ抱いているっていうのは、
多分つまり(c)なわけなのだ。こういう言い方されるとすごい不毛感が募る。
が、そのパワフルなlispでは具体的に何が起きているのか、
lispで書くために必要な発想は何かを、
C++11の人間コンパイラになって学習するのは、必ずしも不毛ではないと思いたい。

まあやればなんだって不毛なことなんてないのだが、
効率は悪いことがわかってきたので、さっさとlispでなんでも書いてみた方がいいとは思う。
一度Schemeで勉強してわからなくなった、
3.3.4節くらいまでをC++11で人間コンパイラとして書いてみて、
そこからSchemeなりCommon Lispなりにスイッチするのがいいのだろうな。


----
template<typename InType>
void setInsert(const InType& input, const List& destination)
{setItem(input,const_cast<List&>(destination));}

//---------abstraction barrier---------
const function<List(int)> makeWithdraw(const List& balanceIn)
{
    const List&& balance(move(balanceIn));
   
    return([balance](const int amount){
         if(value<int>(balance)>=amount){
             setInsert(balance-makeLeaf(amount),balance);
             return(balance);
            
         }
         return(makeLeaf("Insuffcient funds"));
     });
}

const function<function<List(int)>(List)> makeAccount(const List& balanceIn)
{
    const List&& balance(std::move(balanceIn));

    const function<List(int)> withdraw
     =[balance](const int amount){
     if(value<int>(balance)>=amount){
         setInsert(balance-makeLeaf(amount),balance);
         return(balance);
     }
     return(makeLeaf("Insuffcient funds"));
    };
   
    const function<List(int)> deposit
     =[balance](const int amount){
     setInsert(balance+makeLeaf(amount),balance);
     return(balance);
    };
   
    const function<function<List(int)>(List)> dispatch=
     [withdraw,deposit](const List& message){
     if(isEq(message,makeLeaf("withdraw"))){return(withdraw);}
     if(isEq(message,makeLeaf("deposit"))){return(deposit);}
     cerr<<"Unknown request --- MAKE-ACCOUNT"<<endl;
     exit(1);
     return(function<List(int)>([](const int i){return(makeList());}));
    };
    return(dispatch);
}



int main(int argc, char** argv)
{
    const auto W1(makeWithdraw(makeLeaf(100)));
    const auto W2(makeWithdraw(makeLeaf(100)));

    cout<<"(W1 50) = "<<listString(W1(50))<<endl;
    cout<<"(W2 70) = "<<listString(W2(70))<<endl;
    cout<<"(W2 40) = "<<listString(W2(40))<<endl;
    cout<<"(W1 40) = "<<listString(W1(40))<<endl;

    cout<<endl;
    const auto acc(makeAccount(makeLeaf(100)));
    cout<<"((acc 'withdraw) 50) = "
     <<listString(acc(makeLeaf("withdraw"))(50))<<endl;
    cout<<"((acc 'withdraw) 60) = "
     <<listString(acc(makeLeaf("withdraw"))(60))<<endl;
    cout<<"((acc 'deposit) 40) = "
     <<listString(acc(makeLeaf("deposit"))(40))<<endl;
    cout<<"((acc 'withdraw) 60) = "
     <<listString(acc(makeLeaf("withdraw"))(60))<<endl;


    return(0);
}
----
出力
----
(W1 50) = 50
(W2 70) = 30
(W2 40) = Insuffcient funds
(W1 40) = 10

((acc 'withdraw) 50) = 50
((acc 'withdraw) 60) = Insuffcient funds
((acc 'deposit) 40) = 90
((acc 'withdraw) 60) = 30

0 件のコメント :

コメントを投稿