2015-12-02-01-STL書籍の掲載コードをCL-STLで書いてみよう-19
>> Site top >> weblog >> 月別アーカイブ >> 2015年12月のlog >> 2015-12-02-01-STL書籍の掲載コードをCL-STLで書いてみよう-19
最終更新日付:2015/12/02 01:00:00
STL書籍の掲載コードをCL-STLで書いてみよう-19
2015 年 12 月 02 日
今回は、Effective STL の「第40項:ファンクタクラスを変換可能にしよう」を。なんていうか、まぁあまり面白くない感じになってしまったので血圧低めで流していこう。
Widget のポインタを格納したコンテナと、Widget が面白いかどうかを判定する述語関数があるとして、
list<Widget*> widgetPtrs; bool isInteresting( const Widget* pw );
シーケンス内で最初の「面白いやつ」を探すには以下のようにする。教科書通りだ。
list<Widget*>::iterator i = find_if( widgetPtrs.begin(), widgetPtrs.end(), isInteresting ); if( i != widgetPtrs.end() ) { //... }
で、問題は次。最初の「面白くないやつ」を探そうとして以下のようにするとコンパイルに通らないよ、という話だ。これが今回の主題だ。
list<Widget*>::iterator i = find_if( widgetPtrs.begin(), widgetPtrs.end(), not1( isInteresting ) );
理由ははっきりしている。isInteresting が関数だからだ。not1 は unary_negate を作成するためのヘルパ関数だが、パラメータが STL の規約に沿った typedef を備えたファンクタクラスでない場合、必要な型情報を引っ張ってくることができない。これが「ファンクタクラスが変換可能か否か」という問題だ。ちなみに、関数を直接「変換可能」にすることはできないから、ひとまずは ptr_fun を使う。以下のように。
list<Widget*>::iterator i = find_if( widgetPtrs.begin(), widgetPtrs.end(), not( ptr_fun( isInteresting ) ) ); if( i != widgetPtrs.end() ) { //... }
書籍ではこの後もいくつかコードが出てくるが、ここでは取りあげない。というのも、CL-STL ではこれらのことが問題にならないからだ。どういうことか、以下に示そう。まず、簡単のために整数を扱うとして、「10以上で7の倍数なら面白い」とみなす関数 is-interesting を用意する。深い意味はないよ。
(defun is-interesting (v) (and (<= 10 v) (zerop (mod v 7))))
で、これを普通に使った様子が以下。何の問題もない。そりゃそうだよね。
(let ((c (new stl:list #{10 11 12 13 14 15}))) (let ((i (stl:find-if (stl:begin c) (stl:end c) #'is-interesting))) (if (_/= i (stl:end c)) (_* i) :not-found))) ;=> 14
では、「面白くない最初の整数」を検索するために以下のようにする。C++ の例では、ptr_fun を使わなければコンパイルに通らなかったものだ。しかし CL-STL では問題なく実行される。
(let ((c (new stl:list #{14 21 28 34 42}))) (let ((i (stl:find-if (stl:begin c) (stl:end c) (stl:not1 #'is-interesting)))) (if (_/= i (stl:end c)) (_* i) :not-found))) ;=> 34
‥‥‥まぁ、当然と言えば当然だ。静的な型チェックをしていないのだから。そういうわけなので、CL-STL では ptr_fun のようなアダプタを使わなくても関数を not1 に渡すことができるのだ。
こうなると、CL-STL では「ファンクタでなく関数」を使う方になおのこと誘導される。Effective STL の別の項ではパフォーマンスの観点から「関数よりもファンクタ」をお勧めしているが、CL-STL ではその利点もない。いよいよファンクタを使う積極的な理由が見当らないのだ。嗚呼。
まぁ、それはそれでいいのかもしれない。CL-STL は Common Lisp で STL を使いたいと思ったから作り始めたものであって、Common Lisp を C++ にしようという試みではないのだ。言語が違えば、同じ問題に対する最適解も違うというものだろう。
このシリーズのこれまでのエントリ
- 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項:ファンクタクラスを変換可能にしよう
コメント
このページのタグ
Page tag : STLとその移植
Page tag : Common Lisp
Copyright(C) 2005-2019 project-enigma.
Generated by CL-PREFAB.