compose & pipe function


Posted by TempuraEngineer on 2023-12-06

目錄


pipe function

pipe function是接收一個或多個function,也是higher-order function的一種

它會由左而右將function組合起來,其中最左邊的function可以接收任意數量的參數

一但最左側的function回傳了值其右邊的function會接收這個回傳值,並進行一些計算後再回傳另一個值直到所有function都跑過

type CalculateFunction = (originalNumber:number, operatedNumber?:number) => number;

const pipe = (...fns: CalculateFunction[]) => {
    return (...nums: number[]) => {
        return fns.reduce((res, currFn, index) => {
            // console.log(res, nums[index]);
            return currFn(res, nums[index]);
        }, 0);
    }
}
const add = (a:number, b:number = 0) => a + b;
const divide = (a:number, b:number = 0) => a - b;
const multiply = (a:number, b:number = 1) => a * b;
const halfOff = (a:number) => a / 2;
const square = (a:number) => Math.pow(a, 2);
const getSquareRoot = (a: number) => Math.sqrt(a);

console.log(pipe(add, add, add, add)(1,2,3,4)); // 1+2+3+4
console.log(pipe(add, add, multiply, divide)(1,2,3,4)); // // (1+2)*3-4


compose function

compose function大致上和pipe function一樣,只差在順序是右而左

const compose = (...fns: CalculateFunction[]) => {
    return (...nums:number[]) => {
        return fns.reduceRight((res, currFn, index) => {
            // console.log(res, nums[index]);
            return currFn(res, nums[index]);
        }, 0);
    }   
}

用起來會像這樣

console.log(compose(1, 2, 3, 4)); // 4+3+2+1
console.log(compose(add, add, multiply, divide)(1,2,3,4)); // ((0-4)*3)+1+2


為什麼要用pipe/compose function

如果不用pipe/compose的話,當要進行一連串的處理時,就會得到一段非常巢狀的可讀性低的code,且可讀性會隨著function數量增加而持續下降

// 相當於compose(1, 2, 3, 4)
console.log(add(add(add(1,2),3),4));


// 相當於pipe(add, add, multiply, divide)(1,2,3,4)
console.log(divide(multiply(add(1,2), 3),4));


value wrapper

value wrapper就是function chain,顧名思義就是用.把function連在一起,變得像是chain

在jQuery或者d3之類的library都會看到這個做法

const pipe = (value) => ({
  value,
  to: cb => pipe(cb(value))
})
const pipe = <T extends number | string>(value:T) => ({
  value,
  to: (cb:(d:T) => T) => pipe(cb(value))
})

console.log(
    pipe<number>(1)
    .to((n) => add(n, 2))
    .to((n) => add(n, 3))
    .to((n) => add(n, 4))
    .value
);

console.log(
    pipe<number>(1)
    .to((n) => add(n, 2))
    .to((n) => multiply(n, 3))
    .to((n) => divide(n, 4))
    .value
);
// 修改成currying function
const add = (a:number) => (b:number) => b + a;
const divide = (a:number) => (b:number = 0) => b - a;
const multiply = (a:number) => (b:number) => b * a;
const halfOff = (a:number) => a / 2;

const pipe = <T extends number | string>(value:T) => ({
  value,
  to: (cb:(d:T) => T) => pipe(cb(value))
})

console.log(
    pipe<number>(1)
    .to(add(2))
    .to(add(3))
    .to(add(4))
    .value
);

console.log(
    pipe<number>(1)
    .to(add(2))
    .to(multiply(3))
    .to(divide(4))
    .value
);


參考資料

Higher-Order Functions(HoF) in JavaScript - Explain Like I'm Five
Compose and Pipe in JavaScript
A quick introduction to pipe() and compose() in JavaScript
Function Piping in JavaScript


#Functional Programming #compose #pipe







Related Posts

歡樂學 Python 位元組碼(byte code)

歡樂學 Python 位元組碼(byte code)

如何用 ROS Topic 控制機器人移動

如何用 ROS Topic 控制機器人移動

[ JS筆記 ] JavaScript 物件導向 - 基礎介紹

[ JS筆記 ] JavaScript 物件導向 - 基礎介紹


Comments