2015-10-14-01-STL書籍の掲載コードをCL-STLで書いてみよう-9
>> Site top >> weblog >> 月別アーカイブ >> 2015年10月のlog >> 2015-10-14-01-STL書籍の掲載コードをCL-STLで書いてみよう-9
最終更新日付:2015/10/14 01:00:00
STL書籍の掲載コードをCL-STLで書いてみよう-9
2015 年 10 月 14 日
引き続き Effective STL、今回は第30項の「出力先範囲の大きさを確認しよう」から。
この項では、まずやっちゃいけない例から始まる。transform で新しいシーケンスを生成するが、格納先コンテナの end を指定しちゃってるパターンだ。
int transmogrify( int x ); std::vector<int> values; ... std::vector<int> results; std::transform( values.begin(), values.end(), results.end(), transmogrify); // BUG!
CL-STL で同じミスをしでかすことはもちろん可能だ。以下のようになる。これを実行した場合の結果は未定義の領域なので、何が起きてもびっくりしないように。
(defun transmogrify (x) (* x 2)) (let ((values (new stl:vector #{0 1 2 3 4 5 6 7 8 9})) (results (new stl:vector))) (stl:transform (stl:begin values) (stl:end values) (stl:end results) #'transmogrify)) ;; BUG!
で、続いてこれを正しく修正したコードが提示されるわけだ。具体的には、出力先の反復子でなく後方挿入反復子を指定するところが変更点。
std::vector<int> results; std::transform( values.begin(), values.end(), std::back_inserter(results), transmogrify);
これに対応する CL-STL コードは以下の通り。これなら実行できる。
(let ((values (new stl:vector #{0 1 2 3 4 5 6 7 8 9})) (results (new stl:vector))) (stl:transform (stl:begin values) (stl:end values) (stl:back-inserter results) #'transmogrify) (stl:shrink-to-fit results) (stl:data results)) ;=> #(0 2 4 6 8 10 12 14 16 18)
続いて、front_inserter を使用する話に移る。push_front ができなければならないので、対象は deque と list だ。
std::list<int> results; std::transform( values.begin(), values.end(), std::front_inserter(results), transmogrify);
対応する CL-STL のコードは以下。書籍でも説明されている通り、逆順に入ることになる。
(let ((values (new stl:vector #{0 1 2 3 4 5 6 7 8 9})) (results (new stl:list))) (stl:transform (stl:begin values) (stl:end values) (stl:front-inserter results) #'transmogrify) (stl:for (v results) (format t "~A " v))) ;=> 18 16 14 12 10 8 6 4 2 0 NIL
これを解決するには、逆方向反復子を使用する。begin/end のかわりに rbegin/rend を使用しているだけだ。
(let ((values (new stl:vector #{0 1 2 3 4 5 6 7 8 9})) (results (new stl:list))) (stl:transform (stl:rbegin values) (stl:rend values) (stl:front-inserter results) #'transmogrify) (stl:for (v results) (format t "~A " v))) ;=> 0 2 4 6 8 10 12 14 16 18 NIL
さらに続いて insert_iterator を使う話。すでにシーケンスを保持しているresults の中央に要素を割り込ませる。
vector<int> values; ... vector<int> results; ... std::transform( values.begin(), values.end(), std::inserter( results, results.begin() + results.size() / 2), transmogrify);
対応する CL-STL コードは以下。実はここで insert-iterator にバグがあることが発覚したが、幸いすぐに原因が判明したので修正済み。
(let ((values (new stl:vector #{0 1 2 3 4})) (results (new stl:vector 6 :x))) (stl:transform (stl:begin values) (stl:end values) (stl:inserter results (_+ (stl:begin results) (/ (stl:size results) 2))) #'transmogrify) (stl:shrink-to-fit results) (stl:data results)) ;=> #(:X :X :X 0 2 4 6 8 :X :X :X)
‥‥‥まぁひとまずはこんなところだろうか。このあと、reserve を使ったとしても「容量が増えた」だけで「要素数が増えたわけではない」ため、やっぱり挿入反復子を使わなきゃダメだよ、という話が出る。まぁそのあたりのコトはいいだろう。今回はこれでおしまい。
このシリーズのこれまでのエントリ
- 2015-09-19 - 第47項:書き込み専用コードの作成は避けよう
- 2015-09-20 - 第9項:消去オプションは注意して選択しよう
- 2015-09-22 - 第9項:消去オプションは注意して選択しよう - 2
- 2015-09-28 - 第9項:消去オプションは注意して選択しよう - 3
- 2015-09-30 - 第14項:reserve を使って不必要な割り当てを避けよう
- 2015-10-01 - 第17項:余分な容量を取り除くには「swap技法」を使おう
- 2015-10-09 - 第27項:コンテナのconst_iteratorをiteratorに変換するには、distanceとadvanceを使おう
- 2015-10-12 - 第5項:単一要素メンバ関数より範囲メンバ関数を使おう
- 2015-10-14 - 第30項:出力先範囲の大きさを確認しよう
コメント
このページのタグ
Page tag : STLとその移植
Page tag : Common Lisp
Copyright(C) 2005-2021 project-enigma.
Generated by CL-PREFAB.