【在主畫面加入捷徑】
       
【選擇語系】
繁中 简中

[C 語言] 程式設計教學:撰寫函數式程式 (Functional Programming)

Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email
【贊助商連結】

    函數式程式由函式組成程式,具有主流程式較少見到的特性,像是儘量避免狀態改變、把函式當成值來使用等。C 語言不是函數式語言,但仍有少數函數式程式的特性。本文中介紹一些在 C 語言中可見的函數式程式特性,這些寫法不是主流的手法,故僅供參考。

    閉包 (Closure)

    C 語言沒有真正的閉包,但透過靜態局部變數 (static local variable) 可以模擬有狀態的函式 (stateful function),即閉包 (closure)。例如以下用來生成質數 (prime number) 的函式:

    unsigned prime(void)
    {
        static unsigned n = 1;
    
        while (true) {
            n += 1;
            bool is_prime = true;
            
            unsigned i = 2;
            while (i < sqrt(n)) {
                if (n % 2 == 0) {
                    is_prime = false;
                    break;
                }
            
                i += 1;
            }
            
            if (is_prime)
                goto END;
        }
        
    END:
        return n;
    }

    承上,可用此函式求小於等於 100 的質數:

    unsigned n;
    while ((n = prime()) <= 100)
        printf("%u\n", n);

    這個手法可行的原因在於靜態局部變數的狀態在每次函式呼叫後會保留下來,下次函式呼叫時會從先前的狀態恢復,行為上很像閉包。但在多執行緒程式中,使用靜態局部變數問題較多,故這個寫法僅供參考。

    匿名函式 (Anonymous Function)

    標準 C 缺乏匿名函式 (anonymous function) 的特性,以下範例使用 GCC 的延伸功能來實作匿名函式:

    #include <assert.h>
    
    // Generic lambda function
    #define lambda(type, body) ({ \
            type __fn__ body; \
            __fn__; \
        })
    
    int main(void) {
        assert(lambda(int, (int a , int b) { 
            return a > b ? a : b;
        })(3, 5) == 5);
    
        return 0;
    }

    由於這不是標準 C 語言的特性,即使 GCC 很常見,也應該慎重使用。這個寫法僅供參考,不用刻意使用這種手法。

    高階函式 (Higher-Order Function)

    高階函式 (higher-order function) 是使用函式為參數或回傳值的函式。例如,以下的 list_select 函式僅保留串列中符合 filter 條件的元素:

    typedef bool (*filterFn) (int);
    
    bool list_select_mut(List **self, filterFn filter)
    {
        assert(*self);
    
        List *out = list_new();
        if (!out) {
            return false;
        }
    
        Node *curr = (*self)->head;
        while (curr) {
            if (filter(curr->data)) {
                if (!list_push(out, curr->data)) {
                    list_free(out);
    
                    return false;
                }
            }
    
            curr = curr->next;
        }
    
        list_free(*self);
        *self = out;
    
        return true;
    }

    在這個函式中,filter 接收一個函式常參數,所以 list_select_mut 是高階函式。但這個範例沒有考慮多執行緒的情境,只是逐一走訪串列物件 self 的元素,故僅供參考。

    【贊助商連結】
    【贊助商連結】
    【分類瀏覽】