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 参考套件

参考资料