五種寫 For Loop 的方法
Posted on April 11th, 2008 at 6:23 by fr3@K

同樣是寫一個迭代的 loop, 至少有下面兩種標準 (C++98), 兩種半標準 (Boost), 以及一種未來式 (C++0x) 的寫法.


  1. 傳統的 for statement
  2. size_t total_size(const list<string>& strings)
    {
      size_t total = 0;
      list<string>::const_iterator first = strings.begin();
      list<string>::const_iterator last = strings.end();
      for(;
          first != last;
          ++first)
      {
        total += first->size();
      }
      return total;
    }

    跟其他的方法相比, 手工打造的 for loop 雖然可能最容易寫, 卻也是最容易寫錯的. 不但要人工確保 firstlast 屬於同一個 sequence,1 更不能忘了要正確地對 first 做 increment.2

  3. for_each 加上手寫的 functor
  4. struct accumulator
    {
      size_t& result;
      accumulator(size_t& result)
      : result(result) {}
    
      accumulator& operator()(size_t value)
      {
        result += value;
      }
    };
    size_t total_size(const list<string>& strings)
    {
      size_t total = 0;
      for_each(
        strings.begin(),
        strings.end(),
        accumulator(total));
      return total;
    }

    這個方法固然避開了方法 1 的缺點. 若大規模的使用, 很可能要寫一堆這種小小的 functor. 不但一點也不優雅, 還很令人困擾.3

  5. 或是 Boost.Foreach
  6. size_t total_size(const list<string>& strings)
    {
      size_t total = 0;
      BOOST_FOREACH(const string& str, strings)
      {
        total += str.size();
      }
      return total;
    }

    Boost.Foreach 是個使用起來簡單明瞭,4 不容易寫錯看起來也漂亮直覺的 macro. 是最佳寫 for loop 的方法之一. 最大的缺點是 - 它是個 macro.

    [Update] Boost.Foreach 還有個之前遺漏掉的優點; 它跟真正的 for statement 的 loop 一樣, 可以中途 break 掉.5

    [Update] 也可以參考這裡那裡類似的 macro.

  7. for_each + Boost.Lambda
  8. size_t total_size(const list<string>& strings)
    {
      size_t total = 0;
      for_each(
        strings.begin(),
        strings.end(),
        boost::ref(total) +=
          boost::lambda::bind(&string::size, boost::lambda::_1));
      return total;
    }

    Boost.Lambda 讓使用者就地產生匿名的 functional object 的功能真的很棒, 免去了寫一堆小小鳥鳥 functor 的糗境. 它不只能與 algorithm 搭配使用, 我就曾在一個 project 大量使用它來就地產生 callback function (event handler).

    缺點是想使用它的使用者一定要先用功 K 過使用手冊.6 更讓人怯步的是在寫錯時 compiler 吐出來的 error message. 即便是對號稱 C++ 高手的 programmer, 在看與它相關的錯誤訊息的時候都常會覺得正在看一段無法解讀的密碼.

    說真的, 除非情非得已, 或是跟我一樣, 不時想要藉由見到鬼的錯誤訊息來自虐一下. 這個方法不見得是最好的選擇. 使用前需先做好心裡建設.

  9. 在不遠的將來更可以用 for_each 配上 C++0x 的 Lambda Expressions and Closures (PDF)
  10. size_t total_size(const list<string>& strings)
    {
      size_t total = 0;
      for_each(
        strings.begin(),
        strings.end(),
        [&](const string& str)
        {
          total += str.size();
        }
      );
      return total;
    }

    先稍微解釋一下. 一對方括號 ([]) 是用來表示接下來是個 lambda expression. 在方括號內部的 & 符號表示要把外部的變數以 reference 的方式抓 (capture) 到 lambda expression 的內部使用. 因此才能參考並修改到外部的 total 變數. Sutter 最近的一篇關於 C++0x 的 blog 有更詳細的說明.

    這方法結合了 Boost.Foreach 的簡單容易讀寫, 更融入了 Boost.Lambda 不只能被使用在 for loop 的優點. 最棒的是錯誤訊息應該不再是見到鬼般的難以解讀, 而會是 programmer readable 了. :P

    等 C++0x 正式成為標準, 得到龍頭 compiler 廠商支援後. 我想這將會是最佳 for loop 的作法.

[Update] 又一個使用 for_each 的有力理由.

  1. 不能利用如 MSVC 的 Debug Iterator Support 在執行時期自動報錯 []
  2. 不要懷疑, 很遺憾就是有這種 bug… []
  3. 至少對我很困擾 []
  4. 實作可不那麼簡單, 有興趣的請自行挖掘 source code []
  5. 也可以 continue, 只是 continue 是很容易模擬的 []
  6. 仔細想想, 好像沒什麼東西是不先好好用功就可以耍得好的 []
del.icio.us:五種寫 For Loop 的方法 digg:五種寫 For Loop 的方法 spurl:五種寫 For Loop 的方法 newsvine:五種寫 For Loop 的方法 furl:五種寫 For Loop 的方法 Y!:五種寫 For Loop 的方法 黑米共享書籤:五種寫 For Loop 的方法 推推王:五種寫 For Loop 的方法
Previous Post
« Knock Knock, You’ve Been Hacked! «
Next Post
» 不是李白也能 C++ »

Zero Comments »

No comments yet.

Comments RSS TrackBack URI

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>