# pipe 和 compose

# pipe

將多個 Function 進行封裝,接著同步的執行。

# 實作介面

const pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x);

// 或

const pipe = (...fns) => (x) => {
  let list = fns.slice();
  while (list.length) {
    x = list.shift()(x);
  }
  return x;
};
1
2
3
4
5
6
7
8
9
10
11

# 範例

const getName = (person) => person.name;
const uppercase = (string) => string.toUpperCase();
const get6Characters = (string) => string.substring(0, 6);
const reverse = (string) =>
  string
    .split('')
    .reverse()
    .join('');

const person = {
  name: 'Buckethead',
};

const pipeData = pipe(getName, uppercase, get6Characters, reverse)(person);
1
2
3
4
5
6
7
8
9
10
11
12
13
14

輸出結果:TEKCUB

# compose

pipe 轉換成另一個方向執行,即從右到左的運作。

# 實作介面

const compose = (...fns) => (x) => fns.reduceRight((v, f) => f(v), x);

// 或

const compose = (...fns) => (x) => {
  const list = fns.slice();
  while (list.length) {
    x = list.pop()(x);
  }
  return x;
};

// 或使用 lazy-evaluation
//...
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 多參數輸入

使用 lazy-evaluation 包裝:

const compose = (...fns) =>
  fns.reduceRight((fn1, fn2) => (...args) => fn2(...fn1(...args)));
1
2

例如,將每個值加三後,再做累加:

const add3 = (...args) => args.map((x) => x + 3);
const total = (...args) => args.reduce((acc, cur) => acc + cur, 0);

compose(total, add3)(1, 2, 3, 4, 5); // 30
compose(total, add3, add3)(1, 2, 3, 4, 5); // 57
1
2
3
4
5
  1. 與其每一次都計算後將結果再進到下一個循環,這次 defer 延遲所有的運算

  2. 每一次的循環都會返回一個包裹層級更多的函式

  3. reduce 最後結果得到一個函式

    function composed(...args){
       return fnN(...fn2(...fn1(...args))
    }
    
    1
    2
    3
  4. 傳入參數後,最終組合函式再由內到外處理參數

# 範例

  • 延伸 pipe 的範例,使用 compose 撰寫

    const composeData = compose(
      reverse,
      get6Characters,
      uppercase,
      getName
    )(person);
    
    1
    2
    3
    4
    5
    6

    輸出結果:TEKCUB

  • 將字串分解 -> 去除陣列重複元素 -> 過濾小於等於 4 長度的值

    function splitString(str) {
      return String(str)
        .toLowerCase()
        .split(/\s|\b/)
        .filter(function alpha(v) {
          return /^[\w]+$/.test(v);
        });
    }
    
    function deDuplicate(list) {
      return Array.from(new Set(list));
    }
    
    function skipShortWords(words) {
      return words.filter((word) => word.length > 4);
    }
    
    const longWords = compose(skipShortWords, deDuplicate, splitString);
    
    const result = longWords(text);
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

# 參考

Monal - 初探篇 (opens new window)

A quick introduction to pipe() and compose() in JavaScript (opens new window)

Last Updated: 2021/2/25 上午8:00:30