2015-09-28-01-STL書籍の掲載コードをCL-STLで書いてみよう-4

>> Site top >> weblog >> 月別アーカイブ >> 2015年09月のlog >> 2015-09-28-01-STL書籍の掲載コードをCL-STLで書いてみよう-4

最終更新日付：2015/09/28 01:00:00

STL書籍の掲載コードをCL-STLで書いてみよう-4

2015 年 09 月 28 日

実は、「Effective STL」の第9項、「消去オプションは注意して選択しよう」のコードを CL-STL で書いてみる試みはまだ終わっていなかったりする。残りは、ループの内部でログを出すという話。

正直なところ、この件についてはちょっとダレてきたので簡単に片付けよう。まず、連想コンテナについて。Effective STL では、これは簡単だとしている。実際、１行追加するだけだ。以下のように。

std::ofstream logFile; AssocContainer< int > c; ... for ( auto i = c.begin(); i != c.end(); /* none */ ) { if ( badValue( *i ) ) { logFile << "Erasing " << *i << "

" ; c.erase( i++ ); } else ++i; }

当然、CL-STL でも同じようになる。ログ出しは面倒なので format で。

* ( let ((c (new stl:set #{1 2 3 4 5 6 7 8 9 10} #'< ))) (with-operators (stl:for (((itr1 (stl:begin c)) (itr2 (stl:end c))) (_/= itr1 itr2) nil ) ( if (bad-value *itr1) ( progn (format t "Erasing ~A~%" *itr1) (stl:erase c itr1++)) ++itr1))) (stl:for (v c) (format t " ~A" v))) ;=> Erasing 3 ; Erasing 6 ; Erasing 9 ; 1 2 4 5 7 8 10 ; NIL

ではシーケンスコンテナではどうか。Effective STL では、remove-copy-if による解決を捨てて、ループに回帰している。その詳細な理由については書籍を見て欲しい。以下のようようなコードになる。

std::ofstream logFile; SeqContainer< int > c; ... for ( auto i = c.begin(); i != c.end(); /* none */ ) { if ( badValue( *i ) ) { logFile << "Erasing " << *i << "

" ; i = c.erase( i ); } else ++i; }

これが CL-STL だと以下のようになる。これは自分でもつまづいた部分なので、最後だしきちっと見てみようか。

* ( let ((c (new stl:vector #{1 2 3 4 5 6 7 8 9 10}))) (with-operators (stl:for (((itr1 (stl:begin c)) (itr2 (stl:end c))) (_/= itr1 itr2) nil ) ( if (bad-value *itr1) ( progn (format t "Erasing ~A~%" *itr1) ( setf itr1 (stl:erase c itr1)) ( setf itr2 (stl:end c))) ++itr1))) (stl:for (v c) (format t " ~A" v))) ;=> Erasing 3 ; Erasing 6 ; Erasing 9 ; 1 2 4 5 7 8 10 ; NIL

上記のコードを書いた時、最初自分は itr2 をループ内で一切更新しないコードを書いてエラーを引き起こし、何がいけないのか理解できずに首をヒネった。しかし、C++ のコードを良く見てみると、ループの終了条件チェックでは i != c.end() とやっているのがわかる。つまり、 ループを１周するたびに都度 end() を取り直しているのだ。それは、ループに使用している反復子を erase の度に更新するのと同じ理由だ。連続メモリコンテナでは end() が指していたを反復子さえ無効化されるのだ。

で、本当に同じことをやる CL-STL のコードは次のようになるだろう。

(stl:for (((itr1 (stl:begin c))) (_/= itr1 (stl:end c)) nil ) ( if (bad-value *itr1) ( progn (format t "Erasing ~A~%" *itr1) ( setf itr1 (stl:erase c itr1))) ++itr1))

しかし、これは許容し難い。C++ & STL であればループの度に end() を実行するのはまぁいいだろう。しかし、それを CL-STL にそのまま移植すると、その度に裏で make-instance が発生することになる。繰り返すが、これはおそらく許容できないだろう。

さて、そんな感じで Effective STL の第9項、「消去オプションは注意して選択しよう」のコードを CL-STL で書いてみるという話はこれでおしまい。次のお題を探さなければ、だな。

コメント

このページにコメントする

このページのタグ

Page tag : STLとその移植

Page tag : Common Lisp

Copyright(C) 2005-2020 project-enigma.

Generated by CL-PREFAB.