2015-12-04-01-STL書籍の掲載コードをCL-STLで書いてみよう-21
>> Site top >> weblog >> 月別アーカイブ >> 2015年12月のlog >> 2015-12-04-01-STL書籍の掲載コードをCL-STLで書いてみよう-21
最終更新日付:2015/12/04 01:00:00
STL書籍の掲載コードをCL-STLで書いてみよう-21
2015 年 12 月 04 日
昨日、「ひとまず1回で終わらせることができたようだ」などと書いたが、あれば誤りだった。最後に書いておきたいことがあったのに忘れていたのだ。今日はそのおつり拾い。
「第43項:独自に作成したループよりアルゴリズムの呼び出しを優先して使おう」の最後では、「アルゴリズムよりループを使う方が明確になることがある」として以下のような例を挙げている。「x より大きく、y より小さな最初の要素を検索する」というものだ。
vector<int> v; int x, y; //... vector<int>::iterator i = v.begin(); for( ; i != v.end(); ++i ) { if( x < *i && *i < y ) break; }
まずは、これを CL-STL で書いておこう。いつも通り、ちゃんと動作するように値を設定している。
(let ((v (new stl:vector #{0 7 6 4 2 3 9 1 8 5})) (x 2) (y 5)) (let ((i (stl:begin v))) (do () ((_== i (stl:end v)) i) (when (< x (_* i) y) (return)) (_++ i)) (if (_== i (stl:end v)) :end (_* i)))) ; => 4
CL-STL の for 構文を使用すれば以下のようにも書ける。こちらの方が原著のコードには近い。
(let ((v (new stl:vector #{0 7 6 4 2 3 9 1 8 5})) (x 2) (y 5)) (let ((i (stl:begin v))) (stl:for (nil (_/= i (stl:end v)) (_++ i)) (when (< x (_* i) y) (return))) (if (_== i (stl:end v)) :end (_* i)))) ; => 4
で、問題はというと、これを STL アルゴリズムで書くとかえってわかりにくいよ、というものだ。Effective STL では以下のコードが提示されている。これは標準外の compose2 などを使っている。
vector<int>::iterator i = find_if( v.begin(), v.end(), compose2( logical_and<bool>(), bind2nd( greater<int>(), x), bind2nd( less<int>(), y )));
まぁ、なんていうか、たしかに直観的に理解できるものではないよね。こういう事例を考えれば、「時には明示的ループを書いた方が良い場合もある」というのもうなずける。
だがしかし。C++ にだってラムダ式が導入されたのだ。それを使えば、以下のようにとても直観的に書けるようになる。素晴らしい。
auto i = std::find_if( v.begin(), v.end(), [x, y] ( const int& v ) { return x < v && v < y; } );
そしてラムダ式だのクロージャだのは Lisp の方が圧倒的に先輩だ。CL-STL でも、以下のように書くことができる。いいねぇ。
(let ((v (new stl:vector #{0 7 6 4 2 3 9 1 8 5})) (x 2) (y 5)) (let ((i (stl:find-if (stl:begin v) (stl:end v) (lambda (n) (< x n y))))) (if (_== i (stl:end v)) :end (_* i)))) ; => 4
そういうわけなので、個人的には「明示的ループの方が明解になる」局面はもはやほとんどないと思っている。コンテナを使い、アルゴリズムを使い、反復子とファンクタ(というよりクロージャ)で糊付けすればソースコードは驚異的に短くなる。くどいようだけれども素晴らしい。
このシリーズのこれまでのエントリ
- 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項:出力先範囲の大きさを確認しよう
- 2015-10-16 - 第31項:ソートの選択肢を知っておこう
- 2015-10-17 - 第31項:ソートの選択肢を知っておこう - 2
- 2015-10-23 - 第32項:本当に削除したい場合は、remove風アルゴリズムの後にeraseを使おう
- 2015-10-25 - 第33項:ポインタのコンテナには注意してremove風アルゴリズムを使おう
- 2015-10-28 - 第34項:ソート済み範囲を必要とするアルゴリズムに注意しよう
- 2015-10-30 - 第36項:copy_ifの正しい実装について理解しよう
- 2015-11-03 - 第37項:範囲に関する要約情報を取得するには、accumulateまたはfor_eachを使おう
- 2015-11-05 - 第37項:範囲に関する要約情報を取得するには、accumulateまたはfor_eachを使おう - 2
- 2015-11-24 - 第39項:述語を純粋関数にしよう
- 2015-12-02 - 第40項:ファンクタクラスを変換可能にしよう
- 2015-12-03 - 第43項:独自に作成したループよりアルゴリズムの呼び出しを優先して使おう
- 2015-12-04 - 第43項:独自に作成したループよりアルゴリズムの呼び出しを優先して使おう - 2
コメント
このページのタグ
Page tag : STLとその移植
Page tag : Common Lisp
Copyright(C) 2005-2019 project-enigma.
Generated by CL-PREFAB.