Prefer for-loop rather than while-loop in simple case.

for loop case

優點:

  • 縮小 index 變數生存範圍。
    跑動的 index 變數像是 i 或是 sample code 中的 string pos,會是大括號中的 local variable,一出 for-loop 即失效,不容易被誤用。

  • 容易找到 index 的定義。
    index 的宣告乖乖在 for 宣告裡頭,很明確地知道是 only for for-loop 的,不會是其他段落的一部份

  • 容易找到 Increment Exression 的敍述。
    遞增 expression 放的位置非常明確,即在 for(;;INCREMENT_EXPRESSION){} ,不會藏在 block 裡頭,容易找到並容易閱讀。

缺點:

  • 不夠有彈性。
    • 許多 cases 並不適用這麼剛好可以塞進 for-loop form 的 model。
    • 譬如
      • 若有 loop 會需要 reference 兩種不同型別的 index,那就無法放進 for-loop 的宣告式裡了。
      • Increment Exression 要在 for-loop 一開始就走過一遍的情況。
      • 之後的 code 可能需要 index 的資訊。ex: 計數器。
      • while loop 能寫成的 case ,用 for-loop 寫反而很囉嗦的 case。
for loop code
std::vector<std::wstring> parent_dirs_by_for(const std::wstring& path)
{
    std::vector<std::wstring> result;
    
    for(auto slash_pos = path.find(L"\\", 0);
          slash_pos != std::wstring::npos;
          slash_pos = path.find(L"\\", slash_pos+1))
    {
        const auto sub_path = path.substr(0, slash_pos);
        result.push_back(sub_path);
    }

    return result;
}

while loop code

while loop code
std::vector<std::wstring> parent_dirs_by_while(const std::wstring& path)
{
    std::vector<std::wstring> result;
    
    auto slash_pos = path.find(L"\\", 0); // index 宣告藏在這裡。好像跟樓下 while loop 不太熟。
    while(slash_pos != std::wstring::npos)
    {
        const auto sub_path = path.substr(0, slash_pos);
        result.push_back(sub_path);
        slash_pos = path.find(L"\\", slash_pos+1); // 遞增 expression 藏在這裡。
    };
    // 跑到這行, slash_pos 意義上早就沒用了,但依然可以修改存取,compiler 也不會警告你。
    return result;
}

結論:
寫 while 是最具有自由的寫法。但 盡量 寫成 for-loop ,可以讓人較容易抓到此 loop 的開頭與遞增方式。但有極簡單的 case 能用 while 寫成,當然是不需要用到 for-loop。

Full Code
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
   
std::vector<std::wstring> parent_dirs_by_while(const std::wstring& path)
{
    std::vector<std::wstring> result;
    
    auto slash_pos = path.find(L"\\", 0);
    while(slash_pos != std::wstring::npos)
    {
        const auto sub_path = path.substr(0, slash_pos);
        result.push_back(sub_path);
        slash_pos = path.find(L"\\", slash_pos+1);
    };
    
    return result;
}
std::vector<std::wstring> parent_dirs_by_for(const std::wstring& path)
{
    std::vector<std::wstring> result;
    
    for(auto slash_pos = path.find(L"\\", 0);
             slash_pos != std::wstring::npos;
             slash_pos = path.find(L"\\", slash_pos+1))
    {
        const auto sub_path = path.substr(0, slash_pos);
        result.push_back(sub_path);
    }

    return result;
}


int main()
{
   const std::wstring path = L"C:\\Users\\ot_chen\\Desktop\\Resource\\Images\\Lion\\Grass Blades.jpg";
   const std::vector<std::wstring> expected = {
        L"C:",
        L"C:\\Users",
        L"C:\\Users\\ot_chen",
        L"C:\\Users\\ot_chen\\Desktop",
        L"C:\\Users\\ot_chen\\Desktop\\Resource",
        L"C:\\Users\\ot_chen\\Desktop\\Resource\\Images",
        L"C:\\Users\\ot_chen\\Desktop\\Resource\\Images\\Lion",
   };
   
   {
       const auto actual = parent_dirs_by_while(path);
       if(expected != actual )
       {
           std::wcout << L"parent_dirs_by_while is Failed..." << std::endl;
           std::copy(actual.begin(), actual.end(), std::ostream_iterator<std::wstring, wchar_t>(std::wcout, L"\n"));
       }
   };
      
   {
       const auto actual = parent_dirs_by_for(path);
       if(expected != actual )
       {
           std::wcout << L"parent_dirs_by_for is Failed..." << std::endl;
           std::copy(actual.begin(), actual.end(), std::ostream_iterator<std::wstring, wchar_t>(std::wcout, L"\n"));
       }
   };
   
   return 0;
}
comments powered by Disqus