函數式編程術語解析

原文鏈接javascript

[TOC]java

Arity

指函數的參數數量,由 -ary 和 -ity 這兩個英文後綴拼接而成:編程

const sum = (a, b) => a + b;
const Arity = sum.length;
console.log(Arity);
// 輸出結果爲 2

Higher-Order Functions

高階函數,此類函數能夠接收其餘函數做爲參數,也能夠返回一個函數做爲返回值:數組

const filter = (predFunc, list) => {
  const results = [];
  list.forEach((listItem) => {
    if (predFunc(listItem)) {
      results.push(listItem);
    }
  });
  return results;
}
// filter 這個函數就是將一個函數做爲參數傳入
// 但這都麼有什麼,主要是後面is函數返回一個函數

const is = (type) => (x) => Object(x) instanceof type;
// is這個函數就是將一個函數做爲返回值返回到下面的調用之中

filter(is(Number), [0, '1', 2, null]);
// 上面函數調用的結果是 [0, 2]

Partial Application

偏函數,在原函數的基礎上預填充(pre-filling)部分參數並返回的新函數:安全

// 下面是一個建立偏函數的輔助函數,下面函數將一個函數和這個函數所須要的除了最後一個參數的參數傳入,返回一個新的函數,這個新的函數的參數爲原函數的最後一個參數
const partial = (func, ...args) => (...moreArgs) => func(...args, ...moreArgs);
// 寫一個將三個數字相加的函數
const add3 = (a, b, c) => a + b + c;
// 預填充 (add3, 2, 3) 三個參數,空置最後一個參數,返回一個新的函數,重點是返回一個新的函數
const fivePlus = partial(add3, 2, 3); // (c) => 2 + 3 + c
fivePlus(4);
// => 9

Currying

柯里化,將一個接收多個參數的函數轉化爲單參數函數的方式,轉化後的函數每次只接收一個參數,而後返回一個新函數,新函數能夠繼續接收參數,直到接收到全部的參數:dom

const sum = (a, b) => a + b;
sum(2, 3);
// => 6
const curriedSum = (a) => (b) => a + b;
curriedSum(40)(2);
// => 42.
const add2 = curriedSum(2);
// (b) => 2 + b
add2(10);
// => 12

Function Composition

函數合成,接收多個函數做爲參數並返回一個新函數的方式,新函數按照傳入的參數順序,從右往左依次執行,前一個函數的返回值是後一個函數的輸入值:編程語言

const compose = (f, g) => (a) => f(g(a));
const floorAndToString = compose((val) => val.toString(), Math.floor);
floorAndToString(121.212121);
// => "121"

Purity

一個純函數須要知足兩個條件,第一是函數的返回值只能由輸入值(函數接收的參數)決定,也就是說純函數接收相同的參數會返回相同的值;第二是純函數不會對自身做用域以外的運行環境產生反作用(side effects),好比說不會改變外部環境中變量的值,這會被認爲是不安全的行爲:
純函數示例:ide

const greet = (name) => "Hi, " + name ;
greet("Brianne")
// => "Hi, Brianne"

Side effects

若是函數或表達式與其自身做用域以外的可變數據(mutable data)發生了讀寫操做,那麼此時函數和表達式就產生了反作用:函數式編程

let greeting;
const greet = () => greeting = "Hi, " + window.name;
// greet() 執行時更改了外部環境的變量
greet();
// => "Hi, Brianne"
// new Date() 是可變數據
const differentEveryTime = new Date();

Point-Free Style

point-free style 是一種不顯式向函數傳遞參數的代碼風格,一般須要柯里化和高階函數來實現:函數

const map = (fn) => (list) => list.map(fn);
const add = (a) => (b) => a + b;
// Not points-free
// numbers 是一個顯式傳遞的參數
const incrementAll = (numbers) => map(add(1))(numbers);
// Points-free
// add(1) 的返回值隱式傳遞給了 map,做爲 map 的 list 參數
const incrementAll2 = map(add(1));

Predicate

斷言,一個返回布爾值的函數:

const predicate = (a) => a > 2;
[1, 2, 3, 4].filter(predicate);
// => [3, 4]

Constant

常量,初始化後不能再次執行賦值操做的數據類型:

const five = 5;
const john = { name: 'John', age: 30 };
// 由於常量不可變,因此下面表達式必定爲 true
john.age + five === ({ name: 'John', age: 30 }).age + (5);

常量具備 referentially transparent 的特性,也就是說將程序中出現的常量替換爲它們實際的值,並不會影響程序的結果。譯者話外:實際上在 JavaScript 中的 const 所聲明的常量並非徹底穩定的,使用 Immutable.js 演示更加恰當:

// 這裏的fromJS(), get()函數都是immutable.js所提供的方法
const five = fromJS(5);
const john = fromJS({name: 'John', age: 30});
john.get('age') + five === ({ name: 'John', age: 30 }).age + (5);

Functor

functor 都擁有 map 函數,而且在執行 map 以後會返回一個新的 functor:

object.map(x => x) === object;
object.map(x => f(g(x))) === object.map(g).map(f);

JavaScript 中最多見的 functor 就是數組類型的實例:

[1, 2, 3].map(x => x);
// => [1, 2, 3]
const f = x => x + 1;
const g = x => x * 2;
[1, 2, 3].map(x => f(g(x)));
// => [3, 5, 7]
[1, 2, 3].map(g).map(f);     
// => [3, 5, 7]

Lift

lift 發生在你將值放入 functor 的時候,若是你將函數 lift 進了 Applicative Functor,那麼就可使用這個函數處理傳遞給這個 functor 的值。某些 lift 的實現擁有 lift 或 liftA2 函數,便於在 functor 上執行相關的函數:

const mult = (a, b) => a * b;
const liftedMult = lift(mult);
// => this function now works on functors like array
liftedMult([1, 2], [3]);
// => [3, 6]
lift((a, b) => a + b)([1, 2], [3, 4]);
// => [4, 5, 5, 6]

lift 一個單參數的函數很是相似於 map 操做:

const increment = (x) => x + 1;
lift(increment)([2]);
// => [3]
[2].map(increment);
// => [3]

Lambda

匿名函數,本質上是一個 value:

function(a){
  return a + 1;
};
(a) => a + 1;
// Lambda 經常使用語高階函數中
[1, 2].map((a) => a + 1);
// = [2, 3]
// Lambda 做爲 value 被賦值給變量
let addOne = (a) => a + 1;

Lazy evaluation

惰性求值,是一種按需執行的求值策略,只有須要某個值時纔會執行相關的表達式。在函數式編程語言中,這一特性可用於構造無限列表。

const rand = function*() {
    while (true) {
        yield Math.random();
    }
}
const randIter = rand();
randIter.next().value;
// 每次執行 next() 函數都會返回一個新的隨機數
// 有且只有在執行 next() 的時候纔會返回新值
// function* 聲明(function關鍵字後跟一個星號)定義一個generator
// (生成器)函數,返回一個Generator對象。
// 學習網址:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/function*

Monad

Monad,是一個擁有 of 和 chain 函數的數據類型,chain 相似於 map,但它會輸出非嵌套形式的結果:

['cat,dog', 'fish,bird'].chain((a) => a.split(','))
// => ['cat', 'dog', 'fish', 'bird']
['cat,dog', 'fish,bird'].map((a) => a.split(','))
// => [['cat', 'dog'], ['fish', 'bird']]

在其餘函數式編程語言中,of 也被稱爲 return,chain 也被稱爲 flatmap 和 bind。

Isomorphism

同構轉換,相同數據下不一樣結構之間的轉換。舉例來講,2D 座標既能夠存儲爲數組 [2, 3] 也能夠存儲爲 { x: 2, y: 3 }:

const pairToCoords = (pair) => ({x: pair[0], y: pair[1]})
const coordsToPair = (coords) => [coords.x, coords.y]
coordsToPair(pairToCoords([1, 2]))
// => [1, 2]
pairToCoords(coordsToPair({x: 1, y: 2}))
// => { x: 1, y: 2 }

Setoid

Setoid,擁有 equals 函數的數據類型,可用於與其餘同類型的數據進行比較。爲 Array 類型添加 equals 函數使其成爲 Setoid:

Array.prototype.equals = (arr) => {
    const len = this.length
    if (len !== arr.length) {
        return false
    }
    for (let i = 0; i < len; i++) {
        if (this[i] !== arr[i]) {
            return false
        }
    }
    return true
}
[1, 2].equals([1, 2])
// => true
[1, 2].equals([0])
// => false

Semigroup

Semigroup,擁有 concat 函數的數據類型,能夠與同類型數據進行合併:

[1].concat([2])
// => [1, 2]

Foldable

Foldable,擁有 reduce 函數的數據類型,能夠將 Foldable 的實例轉換爲其餘數據類型:

const sum = (list) => list.reduce((acc, val) => acc + val, 0);
sum([1, 2, 3]) 
// => 6
相關文章
相關標籤/搜索