Filter is good

Java

import org.apache.commons.collections.*;
CollectionUtils.filter(list, new Predicate() {
  public boolean evaluate(Object o) {
    return !((String)o).isEmpty();
  }
});

PHP

$list = array_filter($list, function($s) {return !empty($s);});

Python

l = [x for x in l if x]

JavaScript

l = _.filter(l, function(s){return s.length;});

憑著直覺寫的,如果不能動的話再說 XD
這種寫法通常好讀又簡單,不過有些語言要寫的囉嗦一點…

初探 TR1 function object

最近的專案常常需要更新 INI 設定檔,而最麻煩的事情莫過於太多零散的設定,你必須要記住 INI 檔設定的 key,再把在某個 object 的 member function 把值抓出來,最後再丟給 WritePrivateProfileString 之類的 function 把值更新進檔案。

所以常常就會寫出一堆噁心的 code 像是。

    
    obj.iniKey = "INI_SERVER";
    obj.iniValue = info.GetServer();
    UpdateByObj(obj);

這樣的話,有幾個設定就要寫幾次,所有的 Key 跟 Value 的相關性都顯得零散,未來要加上新的設定,也非得要再加上類似的 code 才行……。

忽然我就靈機一動,也許可以用 tr1::function 跟 tr1::bind 來做這樣的事情。

    struct proxyConfig
    {
        std::string iniKey;
        std::tr1::function<std::string()> func;
    };

    const proxyConfig configArray [] =
    {
        { "INI_SERVER", std::tr1::bind(&proxyInfo::GetServer, &info) },
        { "INI_PORT",   std::tr1::bind(&proxyInfo::GetPort, &info) },
        { "INI_USER",   std::tr1::bind(&proxyInfo::GetUser, &info) },
        { "INI_PASS",   std::tr1::bind(&proxyInfo::GetPass, &info) },
    };

    for (size_t i = 0; i < sizeof(configArray)/sizeof(configArray[0]); ++i)
    {
        obj.iniKey = configArray[i].iniKey;
        obj.iniValue = configArray[i].func();
        UpdateByObj(obj);
    }

靠著把 INI 的 key 跟 member function 建表就顯得簡單一點,並把更新的程式碼簡化成一份,未來如果要新增不同的設定,也只要更動表中的 INI key 跟所綁定的 function 就好。

不過後來仔細想想,其實只要在建表的時候寫成 info.GetServer() 就可以滿足我的需求了。

而 tr1::function 與 tr1::bind 絕對不是只有這點小技倆而已,真正使用的巧妙應該是 C++ Function Objects in TR1 所提及那種彈性而優雅地將 function 包裹成物件,能有改變的彈性,也能應時應地做出適合的調整。

如果你的編譯器還不支援 tr1,除了換一個之外,你還可以考慮一下 boost,也許更瞭解一點,會讓你的生活變得更好歐。

用 mkdir 確保 shell script 執行的互斥性

有些時候,你不希望一個 script 同時有兩個以上的 instance,這個時候你可能有很多想法,例如利用檢查 pid file 的方法,或者是找一個 lockf(1) 之類的程式幫你處理這些事情。(老實說 FreeBSD 的 lockf(1) 的確蠻方便的,尤其是對於懶惰寫 code 的我來說。)

但是這會有另外一個問題,不是每個 OS 都有提供 lockf(1),有些就只給你 lockf(2)。所以我就得換個方法作,首先,我想到這樣的寫法

if [ -f $lockfile] ; then 
    touch $lockfile 

利用一個 lockfile 來確保互斥性,只不過 -f 與 touch 之間還是有機會產生 race condition。結果在讀了一些文章之後,我發現有人說 mkdir(2) 是 atomic 的(我目前還沒有找到什麼佐證的資料,NFS 就先放一旁吧……),也有人就用 mkdir(1) 來確保同時只會有一個 shell script 的 instance,再搭配 trap 來處理一些 signal,這樣似乎就可以暫時取代 lockf 了……。

最後的版本就像這樣,應該還有很多問題,但先這樣吧。

ToLock()
{
        progName=`/bin/basename $0`
        lockname="/tmp/$progName.lock"

        if /bin/mkdir "$lockname"
        then
                # Remove lock directory when script terminates
                trap '/bin/rmdir "$lockname"' 0 1 2 3 15
                trap "exit 2" 1 2 3 15
        else
                echo >&2 "another $progName is running?"
                exit 1
        fi
}

最後附上幾篇參考資料

你今天 static code analysis 了嗎?

最近才稍微看了一下手邊專案的程式碼,不能說有什麼太多的問題,只是就制度面而言,其實還有更多可以改善的地方。

公司以往的作法是這樣,你先 check-in code,然後等到一個禮拜的 static code analysis 再進行全面性的更正。但我想 check-in 之後的 static code analysis 只能說是最後一道防線,在這之前,就只能端看開發者的自由心證。

所以,如果要等公司的工具告訴你問題之前,為什麼不先想辦法先作一次呢?自己先作一次有幾點好處。

  • 確保 check-in 的 code 跑出來的 unit test 結果正確,降低事後修正造成的副作用
  • 減少 submit log 上太多與 feature 無關的 log(例如 strcpy fixing)

而現有的 static code analysis 的工具你可以參考 List of tools for static code analysis 然後選擇用的順手的一個,以 C/C++ 而言,我剛剛稍微玩了一下 RATSflawfinder,他們的確能夠提供一些有用的資訊,像是不該使用 gets,或是使用 strcpy 會造成的危險他都能一一列出。

然而 static code analysis 就真的足夠了嗎?我想答案勢必是否定的。

工作之外更需要學習

星富哥說工作的本身會榨乾一個人的活力,但我覺得工作之於我,工作並不是榨乾我的能力,而是太多瑣碎的事物使我無法更專注地學習。我好像能夠看見幾年後的自己,若是繼續維持這樣的生活下去,我就只是這樣而已,沒有進步,雖然經歷過幾個專案,或是幾個產品,我技術上是沒有進步的。

不過也因為現在的工作必須廣泛閱讀一些東西,像是我選修的一門課在探討跨平台的程式開發,讓與會的每個人分享閱讀 Cross-Platform Development in C++: Building Mac OS X, Linux, and Windows Applications 這本書,原本我想選關於 XUL 還有 XPCOM 的主題,結果這兩個主題很快就被我同事選走了。我最後因緣際會選了 Install 的章節,昨天在捷運上翻了一下,也包含許許多多有趣的主題,像是 XPInstallNSIS

結果這本書講了很多,Tinderbox、Autotools、wxWidgets 也許還有 CMake、GNUstep 等等的東西,但是這些東西在工作上都不見得會出現,工作常常是需要解決舊有產品的問題,這些有趣的東西就只能留待下班之後,或是假日時間。我會希望自己能好好的學習,像是工作需要學習的 python,我也能必須更廣泛的閱讀,思考是不是有更好的解決方案。否則我只是快速的解決一個問題,而並非創造。

我想這是我所需要的,所以我選擇在工作之餘再進行學習,一方面是興趣,一方面也是想不要太快被取代吧。

p.s.
Learning Python Design Patterns Through Video Lectures