Functional Programming 函式編程

Node.js Functional Programming 函式編程

教學影片

什麼是 Functional Programming 函式編程?

Functional Programming 簡稱 FP

  • a programming paradigm 程式規範
  • Style / Pattern 一種撰寫風格
  • Mindset 抽象式思維

所以不管是什麼程式語言,都可以用 Functional Programming 的方式去撰寫

案例

非 Functional Programming

所有變數都在外部可以隨意存取

所有變數都在外部可以隨意存取,所以這個不是 Functional Programming

// 非 Functional Programming
let name = `KJ`;
let greeting = `Hi, I'm`;

// Hi, I'm KJ
console.log(`${greeting} ${name}`);

外部變數與函式互相耦合

外部變數會影響到函式內部,所以這個不是 Functional Programming

// 非 Functional Programming
let name = `KJ`;
let greet = () => {
    let greeting = `Hi, I'm`;
    return `${greeting} ${name}`;
}

// Hi, I'm KJ
console.log(greet());

是 Functional Programming

沒有任何外部變數會影響到函式內部

整個函式的處理邏輯都包含在 Function 函式 中,然後沒有任何外部變數會影響到函式內部,所以只要傳入值相同,輸出的值也必定相同

// 是 Functional Programming
let greet = (name) => {
    let greeting = `Hi, I'm`;
    return `${greeting} ${name}`;
}

let name = `KJ`;

// Hi, I'm KJ
console.log(greet(name));

函式產生器

產生一個指定特性的函式,沒有任何外部變數會影響到內部,所以是 Functional Programming

// 是 Functional Programming
// 形容詞函式產生器
let makeAdjectifier = (adjective) => {
    return (adjectiveTarget) => {
        return `${adjective} ${adjectiveTarget}!!`;
    }
}

let Coolifier = makeAdjectifier('Cool');
let Niceifier = makeAdjectifier('Nice');

let name = 'KJ';
// Cool KJ!!
console.log(Coolifier(name));
// Nice KJ!!
console.log(Niceifier(name));

如何寫出 Functional Programming 程式

避免讓變數 immutable (可變的),使用 immutability (不可變的) 的變數

使用 immutable (可變的) 方式導致變數被異動

變數被異動,會影響到所有使用者個變數的地方,所以會導致程式之間會相互影響,所以這個不是 Functional Programming

// 非 Functional Programming
let Employee = ['Kay', 'Jay', 'KJ'];
Employee[1] = 'Apple';
// [ 'Kay', 'Apple', 'KJ' ]
console.log(Employee);

使用 immutability (不可變的) 方式產生新的變數結構

因為 Array.map() 會根據邏輯產生一個新的陣列,這個陣列與原本的陣列變數是完全不一樣的資料

所以如果變數有做任何的修改,完全不會影響到彼此,所以可以保證函式出來的邏輯資料會一致

let Employee = ['Kay', 'Jay', 'KJ'];
let NewEmployee = Employee.map((currentValue) => {
    if (currentValue == 'Jay') {
        return 'Apple';
    } else {
        return currentValue;
    }
});

// [ 'Kay', 'Jay', 'KJ' ]
console.log(Employee);
// [ 'Kay', 'Apple', 'KJ' ]
console.log(NewEmployee);

// ...
// doSomethineChangeEmployee(Employee);
// doSomethineChangeNewEmployee(Employee);

程式易讀性

撰寫邏輯時,會將所有條件寫在條件判斷式當中,一但判斷變多,邏輯變複雜,就變得難以閱讀,難以維護

let calculationBMI = (bmi) => {
    if(bmi < 0) {
        return '資料錯誤'
    } else if(bmi < 18.5) {
        return '過輕'
    } else if(bmi >= 18.5 && bmi < 24) {
        return '正常'
    } else if(bmi >= 24 && bmi < 27) {
        return '過重'
    } else if(bmi >= 27 && bmi < 30) {
        return '輕度肥胖'
    } else if(bmi >= 30 && bmi < 35) {
        return '中度肥胖'
    } else{
        return '重度肥胖'
    }
}

// 正常
console.log(calculationBMI(20));

使用 Functional Programming 將運算邏輯使用函式的方式包裝

// match 函式先省略
const lessThan = x => bmi => bmi< x ;

const calculationBMI = BMI =>
match(BMI)
  .on(lessThan(0), () => '資料錯誤')
  .on(lessThan(18.5), () => '體重過輕')
  .on(lessThan(24), () => '正常')
  .on(lessThan(27), () => '過重')
  .on(lessThan(30), () => '輕度肥胖')
  .on(lessThan(35), () => '中度肥胖')
  .otherwise(() => "重度肥胖")

// 正常
console.log(calculationBMI(20));

FP 使用大量的 Function,幾乎每個 Function 都可以由更小的 Function 組合出來,例如 lessThan,把邏輯寫在函式名稱當中,就算過了很久後回來看程式,也知道程式大概資料留在幹嘛,也容易維護除錯

這樣好處是可以減少程式碼的重複,所以 FP 的寫法通常比較簡短跟容易。統整 FP 的好處就是容易理解容易改變容易除錯具有彈性

Functional Programming 優缺點

優點

特點 說明
safer 安全 函式不會被外部影響導致發生意料外的結果
easier to debug 容易除錯 函式變數之間不會互相耦合影響,所以不會牽一髮動全身,導致很難追出錯誤問題,是被哪個函式影響到
easier to maintain 容易維護 函式變數之間不會互相耦合影響,所以不會牽一髮動全身,彼此修改的資料,互相影響彼此的邏輯

缺點

特點 說明
儲存空間變大 變數資料為了彼此不會相互影響會複製一份完全新的資料,所以會需要佔比較大的記憶體空間

透過 Functional programming 的觀念可以幫助我們打造出更好理解及維護的程式,當然通常 Functional programming 的寫法效能會相對比較差

但在現今運算技術越來越強的情況下,很多時候這些差異是可以被忽略的 (0.03 秒執行時間是 0.01 秒的三倍,但通常是感覺不出來的),在沒有明顯效能的考量下,誠心建議也推廣大家使用這種 functional programming 的思考方式來寫程式,以寫出更加穩固的程式碼

常見問題

Functional Programming (FP) 函式編程 vs Object Oriented Programming (OOP) 物件導向編程

Functional Programming (FP) 函式編程Object Oriented Programming (OOP) 物件導向編程 沒有哪一個一定是最好的,要根據不同的使用情境去決定要用哪一種寫法

可以盡量以 Functional Programming (FP) 函式編程 的方式去撰寫,因為

  • 容易除錯
  • 容易維護
  • 函式之間不會互相影響

所以若是比較獨立性質的函式邏輯,可以拆分的邏輯,盡量以 Functional Programming (FP) 函式編程 去撰寫會比較適合

但如果遇到需要 類別 Class 繼承的特性,根據不同參數屬性,去延伸調整類別功能,那就適合使用 Object Oriented Programming (OOP) 物件導向編程

但是也可以盡量保持,不同的類別 彼此不要互相相依影響,除了 相互繼承的類別 不得不會因為參數的異動而相互影響外,讓 沒有相互繼承的類別 彼此不互相依賴耦合,維持像是 Functional Programming (FP) 函式編程 的特性彼此獨立

這樣在需要維護或是除錯時,可以將問題範圍,限制在關聯的物件 當中即可,避免出現改 A 壞 B,改 B 又壞 C

Functional Programming 參考套件

參考資料