程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 像寫函數式語言代碼一樣寫C++

像寫函數式語言代碼一樣寫C++

編輯:C++入門知識

忘記最早接觸函數式編程語言是什麼時候了,也忘記接觸的第一門函數式語言是哪一門。斷斷續續接觸過好幾種函數式語言(當然都算不純的,ruby/lisp不算純吧),這些語言的思想在潛移默化中多多少少對我有所影響。

我是個C++程序員,我不知道我平時寫的都是些什麼代碼。最讓人印象深刻就是我會經常寫遍歷STL容器的代碼,是經常,這樣的遍歷你可能也不陌生:

for (ListType::iterator it = con.begin(); it != con.end(); ++it) {
    something
}
或者針對std::map/set等的查找:

Table::iterator it = table.find(key);
if (it == table.end())
    do-something
do-something
多虧STL接口的一致性,這讓我們寫出了很多“一致性“代碼。慢慢地我覺得惡心,不禁想起函數式編程語言中,對於這種需求一般都會提供類似的接口:

con.map(function (it) if (it->some-filed == some-value) return something end)
# 或者
con.each do |it| if it.some-filed == some-value then return something end end
# 或者
(con.map (lambda (it) (if ((= it.some-filed some-value)) (return something))))
(好吧,lisp我又忘了)總之,這種針對容器的遍歷操作,都會成為一種內置接口,並且通過lambda來讓用戶直接編寫處理代碼,少去寫循環的冗余。然後,我寫了類似下面的一組宏(隨手敲的不保證能運行):

#define IT_N __it

#define TRAVERSE_MAP(type, map, exps) \
    for (type::iterator IT_N = map.begin(); IT_N != map.end(); ++IT_N) { \
        exps; \
    }
#define I_KEY (IT_N->first)
#define I_VALUE (IT_N->second)

#define TRAVERSE_LIST(type, list, exps) \
    for (type::iterator IT_N = list.begin(); IT_N != list.end(); ++IT_N) { \
        exps; \
    }
#define L_VALUE (*IT_N)

#define FIDN_MAP_ITEM(type, map, key, fexps, texps) \
    do { \
        type::iterator IT_N = map.find(key); \
        if (IT_N == map.end()) { \
            fexps; \
        } else { \
            texps; \
        } \
    } while(0)

#define VAL_N __val
#define FIND_LIST_ITEM_IF(type, list, cmp, fexps, texps) \
    do { \
        struct Comp { \
            bool operator() (const type::value_type &VAL_N) const { \
                return cmp; \
            } \
        }; \
        type::iterator IT_N = std::find_if(list.begin(), list.end(), Comp()); \
        if (IT_N != list.end()) { \
            texps; \
        } else { \
            fexps; \
        } \
    } while(0)

#define NULL_EXP ;
當然,以上接口都還包含一些const版本,用於const容器的使用。使用的時候(截取的項目中的使用例子):

TRAVERSE_MAP(TimerTable, m_timers,
        I_VALUE.obj->OnTimerCancel(I_KEY, I_VALUE.arg);
        TIMER_CANCEL(I_VALUE.id));

TRAVERSE_LIST(AreaList, areas,
        ids.push_back(L_VALUE->ID()));

FIND_MAP_ITEM(PropertyTable, m_properties, name,
        LogWarn("set a non-existed property %s", name.c_str()); return NIL_VALUE,
        if (val.Type() != I_VALUE.type()) {
            return NIL_VALUE;
        } else {
            GValue old = I_VALUE;
            I_VALUE = val;
            return old;
        });

多虧了C/C++宏對一切內容的可容納性,可以讓我往宏參數裡塞進像if這種復合語句,甚至多條語句(例如最後一個例子)。這些宏我使用了一段時間,開始覺得挺爽,很多函數的實現裡,我再也不用寫那些重復的代碼了。但是後來我發覺這些代碼越來越惡心了。最大的弊端在於不可調試,我只能將斷點下到更深的代碼層;然後就是看起來特不直觀,連作者自己都看得覺得不直觀了,可想而知那些連函數式編程語言都不知道是什麼的C++程序員看到這些代碼會是什麼心情(可以想象哥已經被詛咒了多少次)。

函數式語言讓人寫出更短的代碼,這一點也對我有影響,例如我最近又寫下了一些邪惡代碼:

// split a string into several sub strings by a split character i.e:
// "a;b;c;" => "a", "b", "c"
// "a;b;c" => "a", "b", "c"
std::vector<std::string> SplitString(const std::string &str, char split) {
    std::vector<std::string> ret;
    size_t last = 0;
    for (size_t pos = str.find(split); pos != std::string::npos; last = pos + 1, pos = str.find(split, last)) {
        ret.push_back(str.substr(last, pos - last));
    }
    return last < str.length() ? ret.push_back(str.substr(last)) : 0, ret;
}
惡心的就是最後那條return語句,因為我需要處理”a;b;c”這種c後面沒加分隔符的情況,但我並不願意為了這個需求再寫一個會占超過一行的if語句。因為,我太喜歡ruby裡的if了:


do-something if exp
也就是ruby裡允許這種只有一行if的代碼將if放在其後並作為一條語句。我的不願意其實是有理由的,在c/c++中有太多只有一行條件體的if語句,對這些語句參合進編程風格/可讀性進來後,就不得不讓你寫出不安的代碼,例如:

if (something) return something; // 某些編程風格裡不允許這樣做,因為它不方便調試

if (something)
    return something; // 某些風格裡又有大括號的統一要求

if (something) {
    return something; // 就算符合風格了,但這一條語句就得多個大括號
}

if (something)
{
    return something; // 某些風格裡這大括號就更奢侈了
}
這個return除了乍看上去有點糾結外,其實也不算什麼大問題,但是那個問號表達式返回的0實在沒有任何意義,而正是沒有意義才會讓它誤導人。本來我是可以寫成:

return last < str.length() && ret.push_back(str.substr(last)), ret;
這樣利用條件表達式的短路運算,代碼也清晰多了。但是,std::vector::push_back是一個沒有返回值的函數,所以。

 作者:loop_in_codes

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved