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

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 08:16:53


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

2015 年 09 月 28 日

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

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

std::ofstream logFile;
AssocContainer<int> c;
...
for( auto i = c.begin(); i != c.end(); /* none */ ) {
    if( badValue( *i ) ) {
        logFile << "Erasing " << *i << "\n";
        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 << "\n";
        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() とやっているのがわかる。つまり、 ループを1周するたびに都度 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-2017 project-enigma.
Generated by CL-PREFAB.