2015-12-04-01-STL書籍の掲載コードをCL-STLで書いてみよう-21 - project-enigma

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 00:00:10


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

 

そういうわけなので、個人的には「明示的ループの方が明解になる」局面はもはやほとんどないと思っている。コンテナを使い、アルゴリズムを使い、反復子とファンクタ(というよりクロージャ)で糊付けすればソースコードは驚異的に短くなる。くどいようだけれども素晴らしい。

 

このシリーズのこれまでのエントリ

 

コメント

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

 

このページのタグ

Page tag : STLとその移植

Page tag : Common Lisp

 

 


Copyright(C) 2005-2017 project-enigma.
Generated by CL-PREFAB.