---
# System prepended metadata

title: Functional Programming 風格的 C 語言實作
tags: [DYKC]

---

---
tags: DYKC
---

# Functional Programming 風格的 C 語言實作
contributed by < `jserv`, `JEFF1216`, `brian208579`, `happyincent` >

## 何謂 Functional Programming？

[Functional Programming](https://en.wikipedia.org/wiki/Functional_programming) (以下簡稱 FP) 是種 programming paradigm (開發典範)，不是 design pattern 也不是 framework，更不是 language。簡單來說，FP 是種以數學函數為中心的「思考方式」與「程式風格」。

[Why Functional Programming Matters](http://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf) 是篇 1984 年的論文，在 1990 年做小幅度修改，主要凸顯出 FP 的優點與重要特質。
* 中文翻譯：[函數式程式設計為什麼至關重要](https://www.byvoid.com/zhs/blog/why-functional-programming)

一般來說，FP 的幾個特點如下:
* 運算用的 function 大多只包含條件式及遞迴呼叫;
* [higher-order functions](https://en.wikipedia.org/wiki/Higher-order_function) 主要有兩個特點：
    * function 可當作參數傳入 function 之中
    * function 可以回傳 function
* 沒有隱含 (implicit) 的 [Side Effect](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) (副作用)
    * 將程式語言的函式視作看作是數學上的函數，乍看似乎沒問題，但副作用的存在不能保證每次呼叫的時候，傳回來的東西都會一樣，也就是程式本身，並非獨立於程式語言其他的部份。所以當程式出錯的時候，開發者需要走訪所有觸及的程式碼，方可確立問題的來源;
    * 在 [Haskell](https://www.haskell.org/) 等純函數語言，副作用是必須「明確」地用型別標示出來，而不是隱含的。副作用本身不必然是 primitive 的操作，可以是由語言的其他構件組成。而如 OCaml 這樣的 FP 語言，甚至提供 (explicit) reference 這樣的型別。所以每次呼叫函數，總可以保證傳回的數值不會變，也因此要寫對程式，也變得比較容易，或者說，較難寫程式但可確保容易寫出對的程式;
* [lazy evaluation](https://en.wikipedia.org/wiki/Lazy_evaluation)
    * 當變數被定義時，不會立即 evaluate，而是等待要使用時才 evaluate 並儲存已計算的結果，所以不會重複 evaluate 同一個定義
    * 因為沒有副作用，所以可以把計算過的值記起來，以便下次再呼叫的時候直接拿來用，也就是 lazy evaluation 的精神。

延伸閱讀: [Functional Programming: Concept & Example](https://hackmd.io/p/rJV1zI6jX/)

程式語言引入函式傳遞是縮減複雜度的手法之一，然而，很多時候很難阻止開發者傳遞幾百或幾千行的函式內容。於是，程式語言的設計者則轉向焦點，思索如何規範開發者撰寫出意圖單一、實作單純的簡明函式，這即是不可變(immutable) 特性。當開發者無法任意更動變數內含值或物件狀態，便只能從既有變數值或物件狀態，來產生新的數值或物件狀態，這不啻貫徹數學上的數學函數嗎？換言之，開發者會將大任務切割為若干小任務，於是可察覺小任務的流程重複性，一旦開發者封裝重複的小任務，隨後即可在呼叫它們時，只專注在真正該關心的運算，並以函式傳遞運算。

若函式實作時貫徹不可變特性，該函式就會被稱為純函式 (pure function)，相對地，就會是具有副作用的函式。值得留意的是，即便在 [Haskell](https://www.haskell.org/) 一類純函數式語言中，也是有副作用的部份，不然就無法接受外界輸入，也不能進行程式對外的輸出。純函數式程式設計規範的是，副作用函式與純函式有個明確的界線，一邊是完全純粹的世界，不純的世界是另一邊；副作用本身對電腦上執行的程式是必要的特徵，它並非萬惡，真正令開發者畏懼的是，開發者搞不清楚這函式是否有副作用，進一步導致他們在不知道的地方改變什麼。

近年 FP 被重新提出且廣泛應用，是基於以下考量： 
1. 硬體單核處理器時脈出現瓶頸，中央處理器的運作時脈已經很難超過 5GHz，因此單執行緒程式速度也面臨瓶頸，但隨著半導體製程進步，單位面積可以塞入更多電晶體，因此處理器可納入更多核 (即 multi-core)。典型 imperative 寫法依賴 [side effect](https://en.wikipedia.org/wiki/Side_effect_(computer_science))，使得多核很難發揮效益，但 FP 沒有 side effect，很容易藉由平行運算，充分運用硬體能力;
2. side effect 寫法很難單元測試，因為不保證每次的結果都ㄧ樣，但 FP 的 pure function 很適合單元測試
3. OOP 的 object composition 需依賴 interface，容易造成 interface 混亂與 over design，但 FP 的 function compostion 不需要額外 interface，只要求你使用 Dataflow 思考，並使用 pure function 避免 side effect 即可

## 用 C 語言實作 FP 風格

在 FP 中，以下三種常見操作，搭配下圖食物對應:
* map(): 配合給定行為，回傳一個陣列或鏈結串列
* filter(): 用以檢索和篩選
* reduce(): 將素材混合在一塊，得到最終結果

![](https://i.imgur.com/jiZVpDx.png)

考慮 [test-functional.c](https://github.com/jserv/Functional-Programming-in-C/blob/master/test-functional.c) 的用法:
```cpp
void print_int(void *v, void *args) {
    printf("[ op:%s ] %d\n", (char *) args, (int) (*(int *) v));
}
void print_char(void *v, void *args) {
    static int count = 0;
    printf("%c ", (char) (*(char *) v));
    if (++count > 10) {
        printf("\n");
        count = 0;
    }
}

bool isOdd(void *v, void *args) {
    return (bool) (*((int *) v) & 1);
}
void *twice(void *v, void *args) {
    int *o = malloc(sizeof(int));
    *o = *((int *) v) * 2;
    return o;
}

int main(int argc, char **argv) {
    gc_init();  // initialize the garbage collector

    iter(map(range(0, 10), twice, NULL), print_int,
         "twice"); /* 0, 2, 4, .., 20 */
    iter(filter(range(0, 10), isOdd, NULL), print_int,
         "odd");                                  /* 1, 3, 5, 7, 9 */
    iter(rangeChar('a', 'z'), print_char, NULL);  /* a, b, c, ..., z */
    iter(rangeChar(' ', 'z'), print_char, NULL);  // won't get printed
    iter(rangeChar('1', 'z'), print_char, NULL);

    gc_collect();

    exit(0);
}
```

執行輸出為:
```
[ op:twice ] 0
[ op:twice ] 2
[ op:twice ] 4
[ op:twice ] 6
[ op:twice ] 8
[ op:twice ] 10
[ op:twice ] 12
[ op:twice ] 14
[ op:twice ] 16
[ op:twice ] 18
[ op:twice ] 20
[ op:odd ] 1
[ op:odd ] 3
[ op:odd ] 5
[ op:odd ] 7
[ op:odd ] 9
a b c d e f g h i j k 
l m n o p q r s t u v 
w x y z 1 2 3 4 5 6 7 
8 9 : ; < = > ? @ A B 
C D E F G H I J K L M 
N O P Q R S T U V W X 
Y Z [ \ ] ^ _ ` a b c 
d e f g h i j k l m n 
o p q r s t u v w x y 
z
```

第 25 行的 `map` 操作，將給定 [0, 10] 區間的整數都透過 `twice` 函式乘上 `2`，再透過 `print_int` 個別輸出產生的數值。

第 27 行的 `filter` 操作，依據 `isOdd` 函式的判斷，自 [0, 10] 區間篩選出奇數， 再透過 `print_int` 個別輸出篩選出來的數值。

第 29 行到 31 行則展示 map 搭配 filter，列舉出給定 ASCII 字元集的範圍，並透過 `print_char` 函式輸出。

參見其他實作:
* [Functional Programming](https://www.lucabol.com/tags/functional-programming/)
* [Functional Style Programming Using C](https://www.pluralsight.com/guides/functional-style-programming-using-c)
* [The GObject messaging system](https://developer.gnome.org/gobject/stable/chapter-signal.html)

## 案例: 反轉 linked list 元素

以下展示採用 [Functional Programming](https://en.wikipedia.org/wiki/Functional_programming) 風格開發的 singly-linked list 程式: [reverse.c](https://github.com/jserv/Functional-Programming-in-C/blob/master/reverse.c)

```cpp
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

/* integer linked list type */
typedef struct __int_list const *const int_list_t;
static int_list_t const Nil = NULL; /* empty list */

/* singly linked list element */
typedef struct __int_list {
    int_list_t next;
    int32_t const val;
} node_t;

/* A pointer to where we are about to store the result of computation */
typedef void *const CPS_Result;

/* prototypes for the continuations */
typedef void (*MakeListCallback)(int_list_t list, CPS_Result res);
void make_list(uint32_t const arr_size,
               int32_t const array[],
               int_list_t lst,
               MakeListCallback const cb,
               CPS_Result res);

typedef void (*ReversedListCallback)(int_list_t rlist, CPS_Result res);
void reverse(int_list_t list,
             int_list_t rlist,
             ReversedListCallback const cb,
             CPS_Result res);

typedef void (*VoidMappable)(int32_t const val);
void void_map_array(VoidMappable const cb,
                    uint32_t const size,
                    int32_t const *const arr);

void list2array(int_list_t list, CPS_Result res);

/* reverse a list and then store it in an array */
void reverse_toarray(int_list_t list, CPS_Result res) {
    reverse(list, Nil, list2array, res);
}

static void print_val(int32_t const val) { printf("%d ", val); }

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))

int main(int argc, char *argv[]) {   
    int32_t arr[] = {99, 95, 90, 85, 80, 20, 75, 15, 10, 5};
    uint32_t const arr_size = ARRAY_SIZE(arr);

    int32_t res[arr_size];
    /* call make_list and pass reverse_toarray as the "continuation" */
    make_list(arr_size, arr, Nil, reverse_toarray, res);

    /* print out the reversed array */
    void_map_array(print_val, arr_size, res);
    printf("\n");
    return 0;  
}

/* construct a linked list from an array */
void make_list(uint32_t const arr_size,
               int32_t const arr[],
               int_list_t lst,
               MakeListCallback const cb,
               CPS_Result res) {
    if (!arr_size) {
        cb(lst, res);
        return;
    }
    make_list(arr_size - 1, arr,
        &(node_t){.val = arr[arr_size - 1], .next = lst}, cb, res);
}

/* transform a linked list into an array */
void list2array(int_list_t list, CPS_Result res) {
    if (Nil == list) return;
    int32_t *array = res;
    array[0] = list->val;
    list2array(list->next, array + 1);
}

void reverse(int_list_t list,
             int_list_t rlist, /* reversed list */
             ReversedListCallback const cb,
             CPS_Result res) {
    if (Nil == list) {
        cb(rlist, res);
        return;
    }
    reverse(list->next, &(node_t){.val = list->val, .next = rlist},
            cb, res);
}

/* iterate over an array and performs action cb on each element */
void void_map_array(VoidMappable const cb,
                    uint32_t const size,
                    int32_t const *const arr) {
    if (!size) return;
    cb(arr[0]);
    void_map_array(cb, size - 1, arr + 1);
}
```

精髓在於以下:
```cpp
/* integer linked list type */
typedef struct __int_list const *const int_list_t;
static int_list_t const Nil = NULL; /* empty list */
```

這是個 const 結構指標且該指標的記憶體地址和內含值無法透過區域變數去改變，正如上述所及的 FP 精神。

再來，是關於 callback 的函式指標的理解，這是個函式指標，而 `MakeListCallback` 就是拿來接收 reverse_toarray 的這個函式，並且將 `lst` 和 `res` 傳給 reverse 做下一個動作，打個比方:`make_list(arr_size, arr, Nil, reverse_toarray, res);` 會在這行程式碼去呼叫 `make_list` 副函式直到執行完透過 `cb(lst,res)` 將做好的 linked list 開頭的記憶體地址回傳給 reverse，所以整體的函式執行順序是:

```
main -->
    make_list -->
      reverse_toarray -->
        reverse -->
            list2array -->
                void_map_array
```

再來是建立 linked list 的部分:
這個是從陣列中的尾端建立起，亦即 `5` 是尾巴而 `99` 是頭，而在建立 linked list 過程中，lst 會將下一個結構的 next 儲存起來，並且將記憶體地址和數值複寫進下一個結構中，所以在 make_list 的順序是:

- [ ] lst
 ```graphviz
    digraph foo {
    rankdir=LR;
    node [shape=record];
    a [label="{ <data> 99 | <ref>  }", width=1.2, color=red]
    b [label="{ <data> 95 | <ref>  }"];
    c [label="{ <data> 90 | <ref>  }"];
    d [label="..." shape=box];
    e [label="{ <data> 5 | <ref>  }",width=1.2];
    a:ref:c -> b:ref [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    b:ref:c -> c:ref [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false];
    c:ref:c -> d:ref[arrowhead=dot, arrowtail=vee, dir=both, tailclip=false];
    d->e:ref[arrowhead=dot, arrowtail=vee, dir=both, tailclip=false];
    }
```

- [ ] list
```graphviz
    digraph foo {
    rankdir=LR;
    node [shape=record];
    a [label="{ <data> 99 | <ref>  }", width=1.2, color=red]
    b [label="{ <data> 95 | <ref>  }"];
    c [label="..." shape=box];
    d [label="{ <data> 5 | <ref>  }"];
    e [label="{ NULL }"];

    a:ref:c -> b:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2];
    b:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2];
    c:ref:c -> d:data [arrowsize=1.2];
    d:ref:c -> e [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2];
    }
```

reverse 將開頭 (99) 的 next 指向一開始的 rlist (Null)，再透過 rlist 儲存新的 (99) 的記憶體地址，再依序將 新的 (95) 的 next 指向新的 (99)，如圖:
- [ ] rlist:
```graphviz
    digraph foo {
    rankdir=LR;
    node [shape=record];
    a [label="{ <data> 99 | <ref>  }", width=1.2, color=red]
    b [label="{ NULL }"];

    a:ref:c -> b:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2];

    }
```

```graphviz
    digraph foo {
    rankdir=LR;
    node [shape=record];
    a [label="{ <data> 95 | <ref>  }", width=1.2, color=red]
    b [label="{ <data> 99 | <ref>  }"];
    c [label="{ NULL }"];

    a:ref:c -> b:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2];
    b:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2];
    }
```

直到將全部的 linked list 做完反轉後才結束，如圖:
- [ ] rlist
```graphviz
    digraph foo {
    rankdir=LR;
    node [shape=record];
    a [label="{ <data> 5 | <ref>  }", width=1.2, color=red]
    b [label="{ <data> 10 | <ref>  }"];
    c [label="..." shape=box];
    d [label="{ <data> 99 | <ref>  }"];
    e [label="{ NULL }"];


    a:ref:c -> b:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2];
    b:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2];
    c:ref:c -> d:data [arrowsize=1.2];
    d:ref:c -> e [arrowhead=vee, arrowtail=dot, dir=both, tailclip=false, arrowsize=1.2];
    }
```

最後的 linked list 再傳回 `list2array` 並輸出最終結果。本程式的技巧除了透過陣列來建立一個 linked list，過程中也利用 `if` 的判斷式來進行遞迴並且傳給下一個副函式，例如在 `reverse` 副函式中，其終止的條件是，當 list 的記憶體地址為 NULL (也就是到最後一個 5 時) 會停止遞迴並將其中的值傳給下一個副函式:
```cpp
void reverse(int_list_t list,
             int_list_t rlist, /* reversed list */
             ReversedListCallback const cb,
             CPS_Result res) {
    if (Nil == list) {
        cb(rlist, res);
        return;
    }
    reverse(list->next, &(node_t) K1, cb, res);
}
```

注意到這裡的 CPS:
> Continuation Passing Style (CPS): Programming style characterized by functions with an additional continuation argument, which call their continuation instead of returning to their caller.


## 案例: 對 Linked List 元素進行合併排序

利用 merge sort 我們減少了程式碼中的 side effect 並且將每個副函式幾乎變成了 pure function。也就是說，每個函式我們都只用了區域變數，並讓其中的值只受到該區域的副函式影響，直到結束後傳遞結果為止。

- [ ] merge sort
merge sort 簡單來說就是將一個 linked list 拆成一個又一個獨立的結構再去作排列和重組。如圖：
![](https://i.imgur.com/mZWAlpF.png)

[mergesort.c](https://github.com/jserv/Functional-Programming-in-C/blob/master/mergesort.c)

我們運用兩指標 fast 和 slow，當 fast 不為 `NULL` 時，fast 往前讀取記憶體地址，若下個 fast 依舊不為 NULL，則 slow 和 fast 兩個指標繼續往前，直到 fast 指向 NULL 為止。如下圖:

- [ ] list
```graphviz
    digraph foo {
    rankdir=LR;
    node [shape=record];
    a [label="{ <data> 99 | <ref>  }", width=1.2, color=red]
    b [label="{ <data> 95 | <ref>  }", width=1.2, color=red];
    c [label="{ <data> 85 | <ref>  }", width=1.2, color=red];
    d [label="{ <data> 80 | <ref>  }", width=1.2, color=red];
    e [label="{ <data>. .. | <ref>  }", width=1.2, color=red];
    f [label="{ NULL }", width=1.2, color=red];
    g [label="{ <data> slow }", width=1.2, color=black];
    h [label="{ <data> fast }", width=1.2, color=black];


    a:ref:c -> b:ref [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    b:ref:c -> c:ref [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    c:ref:c -> d:ref [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    d:ref:c -> e:ref [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    e:ref:c -> f [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    a:ref:c -> g:data [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    b:ref:c -> h:data [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    }
   
```

若 fast 不為 NULL 則變成：
```graphviz
    digraph foo {
    rankdir=LR;
    node [shape=record];
    a [label="{ <data> 99 | <ref>  }", width=1.2, color=red]
    b [label="{ <data> 95 | <ref>  }", width=1.2, color=red];
    c [label="{ <data> 85 | <ref>  }", width=1.2, color=red];
    d [label="{ <data> 80 | <ref>  }", width=1.2, color=red];
    e [label="{ <data>. .. | <ref>  }", width=1.2, color=red];
    f [label="{ NULL }", width=1.2, color=red];
    g [label="{ <data> slow }", width=1.2, color=black];
    h [label="{ <data> fast }", width=1.2, color=black];


    a:ref:c -> b:ref [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    b:ref:c -> c:ref [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    c:ref:c -> d:ref [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    d:ref:c -> e:ref [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    e:ref:c -> f [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    a:ref:c -> g:data [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    c:ref:c -> h:data [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    }
   
```

若 fast 仍不為 NULL 的話則 fast 和 slow 各前進一格。
```graphviz
    digraph foo {
    rankdir=LR;
    node [shape=record];
    a [label="{ <data> 99 | <ref>  }", width=1.2, color=red]
    b [label="{ <data> 95 | <ref>  }", width=1.2, color=red];
    c [label="{ <data> 85 | <ref>  }", width=1.2, color=red];
    d [label="{ <data> 80 | <ref>  }", width=1.2, color=red];
    e [label="{ <data>. .. | <ref>  }", width=1.2, color=red];
    f [label="{ NULL }", width=1.2, color=red];
    g [label="{ <data> slow }", width=1.2, color=black];
    h [label="{ <data> fast }", width=1.2, color=black];


    a:ref:c -> b:ref [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    b:ref:c -> c:ref [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    c:ref:c -> d:ref [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    d:ref:c -> e:ref [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    e:ref:c -> f [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    b:ref:c -> g:data [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    d:ref:c -> h:data [arrowhead=dot, arrowtail=vee, dir=both, tailclip=false, arrowsize=1.2];
    }
   
```
直到 fast 碰到 `NULL` 為止，則該 list 就會被分為兩個和其他，以「案例: 反轉 linked list 元素」的輸入來說，就是 (99, 90, 95, 6, 85, 20, 75, 15) 一群，(10, 5) 為另一群這樣分下去。直到變成一個獨立的 linked list 後再去連接起來。

本次的程式執行順序為：
> main --> make_list --> mergesort --> partition --> partition --> ........ --> mergesort --> mergelist --> mergesort --> list2array --> void_map_array

採用 non-intrusive 的 linked list 來實現，從程式碼中 
```cpp
typedef struct __list * list_t;
typedef struct __list {
    list_t next;
} node_t;

typedef struct __ele {
    int32_t const val;
    list_t const list;
} ele_t; 
```

我們將 `val` 和 `list` 的值宣告為 `const`，亦即無法對其值進行改變，然後將`next `宣告為一個指標的指標，圖示如下:
```graphviz
    digraph foo {
    rankdir=LR;
    node [shape=record];
    a [label="{ <data> val | <ref> list }", width=1.2, color=red]
    b [label="{ <data> val | <ref> list }", width=1.2, color=red];
    
    g [label="{ <data> next }", width=1.2, color=black];

    g:ref:c -> b:ref [arrowhead=vee, arrowtail=dot, dir=both, tailclip=true arrowsize=0.5];
  
    a:ref:c -> g:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=true, arrowsize=0.5];
  
    }
```

- [ ] Nil

而在程式實現中，我們必須設計一個 Nil 結構參數，其目的是為了在各個函式之間呼叫，我們是以`ele_t `或是`list_t `的結構型態進行回傳資料和遞迴，所以我們無法直接利用 NULL 來作為函式中斷遞迴的判斷，因此我們令`Nil `為`static const ele_t Nil = {.val = 0, .list = &(node_t){ .next = NULL }}; ` 意思是當我們指向 Nil 時候就表示該記憶體地址指向 Null。如圖：
```graphviz
    digraph foo {
    rankdir=LR;
    node [shape=record];
    a [label="{ <data> val | <ref> list }", width=1.2, color=red]
    b [label="{ <data> Nil(0) | <ref> list }", width=1.2, color=red];
    c [label="{ <data> NULL }", width=1.2, color=black];
    g [label="{ <data> next }", width=1.2, color=black];

    g:ref:c -> b:ref [arrowhead=vee, arrowtail=dot, dir=both, tailclip=true, arrowsize=0.5];
  
    a:ref:c -> g:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=true, arrowsize=0.5];
     b:ref:c -> c:data [arrowhead=vee, arrowtail=dot, dir=both, tailclip=true, arrowsize=0.5];
  
    } 
```

- [ ] container_of (ptr, type, member)

`container_of` 這樣的巨集用於 Linux 核心和一些專案中，作用是根據給定的結構體變數其中的一個成員變數的指標，來獲取指向整個結構體變數的指標。舉例來說，給定以下結構體:
```cpp
struct demo_struct {
    type1 member1;
    type2 member2;
    type3 member3;
    type4 member4;
};      
```

假設結構體變數 `demo` 在執行時期的記憶體位置呈現如下圖:
```
     demo
 +-------------+ 0xA000
 |   member1   |
 +-------------+ 0xA004
 |   member2   |
 +-------------+ 0xA010
 |   member3   |
 +-------------+ 0xA018
 |   member4   |
 +-------------+
```

一旦執行程式碼 `container_of(memp, struct demo_struct, type3)`，會使取得的記憶體地址為該結構的初始地址，亦即 `0xA000`，而在作 merge sort 時，前述 fast 和 slow 指標必須是指向該 linked list 結構的初始記憶體地址，才可以對它作 dereference ，否則就會出現記憶體操作的錯誤，因次我們可定義`list_entry` 如下:
 ```cpp
#define list_entry(node, type, member) \
    container_of(node, type, member)
```

例如在 `partition` 中，我們要對將其拆開時，便會使用到:
```cpp
fast = list_entry(head->list->next, ele_t, list);
```

- [ ] mergeLists

最後是關於 `mergeLists` 的實作，類似之前的程式碼，只是在於進行遞迴呼叫時，要用到 `tmp` 來當作暫存器儲存結構的記憶體地址。
```cpp=
    if (a->list->next == NULL) {
        return b;
    } else if (b->list->next == NULL) {
        return a;
    }

    if (a->val <= b->val) {
        mergedList = a;
        ele_t *tmp = mergeLists(list_entry(a->list->next, ele_t, list), b);
        mergedList->list->next = (list_t)(&(tmp->list));
    } else {
        mergedList = b;
        ele_t *tmp = mergeLists(a, list_entry(b->list->next, ele_t, list));
        mergedList->list->next = (list_t)(&(tmp->list));
    }
```

其中第 13 行將回傳 `(a, Nil)` 給 `mergeLists`。

我們假設 a 是上述示意圖 `95` 的結構體，b 是 `90` 的結構體，在比較完大小後，在 `tmp = mergeLists(a, list_entry(b->list->next, ele_t, list));` 中會進行遞迴呼叫，隨即將 a 結構體指標和 Nil 回傳給 mergeLists 函式，經由 if 判斷式後回傳 a 結構體指標給  tmp 最後將 `90` 的 next ，也就是 b->list->next 由原本的指向 Nil 改成指向 `95` 的結構體，以此遞迴下去完成 non-intrusive linked-list 的重組。最後再輸出該 linked list，完成此次 FP 風格的實作。

gdb下的觀察結果：
```shell
breakpoint 4, partition (head=0x7ffffffedee0, front=0x7ffffffedde0, back=0x7ffffffedde8) at mergesort.c:124
124         if (head == NULL || head->list->next == (list_t)(&(Nil.list))) {
(gdb)
129             slow = head;
(gdb)
130             fast = list_entry(head->list->next, ele_t, list);
(gdb)
132             while(fast->list->next != NULL) {
(gdb) p *head
$1 = {val = 99, list = 0x7ffffffeded8}
(gdb) p *fast
$2 = {val = 95, list = 0x7ffffffedf48}
(gdb) p *(head->list->next)
$3 = {next = 0x7ffffffedf48}
(gdb) p *(head->list)
$4 = {next = 0x7ffffffedf58}
(gdb) p &(head->list>next)
No symbol "next" in current context.
(gdb) p &(head->list->next)
$5 = (list_t *) 0x7ffffffeded8
(gdb) p (head->list)
$6 = (const list_t) 0x7ffffffeded8
(gdb) p (head->list->next)
$7 = (list_t) 0x7ffffffedf58
(gdb) p &(fast->list)
$8 = (const list_t *) 0x7ffffffedf58
```