[英]create compose function in functional programming approach
我試圖通過重新創建撰寫來了解撰寫的工作方式。 作為其一部分,我創建了一個簡單的計算器來獲取一個值,並根據該值獲得回報。
https://medium.com/javascript-scene/reduce-composing-software-fe22f0c39a1d#.rxqm3dqje
本質上最終的目標是創建一個可以在下面完成的功能。 https://github.com/ngrx/example-app/blob/master/src/app/reducers/index.ts#L150
很高興:能夠傳遞多個存款值,並且可以隨時間計算復利。
最好有一些評論,以便我了解函數式編程方法的狀況。
(()=>{
// Generic compose function to handle anything
const compose = (...fns) => (x) => {
return fns.reduceRight((acc,fn)=>{
return fn(acc);
}, x)
};
const getInterest = (value) => {
if (value < 1000) {
return 1 / 100
}
if (value < 10000) {
return 2 / 100
}
return 3 / 100;
};
const getDeposit = (value) => {
return value;
};
const calculator = compose(getDeposit, getInterest)
console.log(calculator(1000)) // Should return value of 1000 * interest rate. I currently get 0.03
})();
問題是您永遠不要將兩個值相乘: value和interest 。
因此,您應該將另一個函數傳遞給合成,該函數將乘以之前的兩個結果。
這意味着該函數將需要獲取2個參數,而其他兩個僅接受一個。 通常,一個函數可能需要任何數量的參數。 因此,作曲家應該能夠將足夠的參數傳遞給每個函數。 此外,函數還可能以數組的形式返回多個值。 這些值應可作為鏈中下一個函數的參數使用,同時還要保留所有先前返回的值。
另一件事是,盡管您已實現compose
從右到左執行功能,但是傳遞的函數順序似乎暗示您希望它們從左到右執行,先是getDeposit
然后是getInterest
,即使在您這種情況下,雙向工作。 不過,我還是建議換個位置。
因此,您可以通過以下方法來完成所有工作:
(()=>{ // Generic compose function to handle anything const compose = (...fns) => (...args) => { return fns.reduceRight((acc,fn)=>{ // Call the function with all values we have gathered so far let ret = fn.apply(null, acc); // If function returns a non-array, turn it into an array with one value if (!Array.isArray(ret)) ret = [ret]; // Queue the returned value(s) back into the accumulator, so they can // serve as arguments for the next function call acc.unshift(...ret); return acc; }, args)[0]; // only return the last inserted value }; const getInterest = (value) => { return value < 1000 ? 0.01 : value < 10000 ? 0.02 : 0.03; }; const multiply = (a, b) => a * b; const getDeposit = (value) => value; // Be aware the the rightmost function is executed first: const calculator = compose(multiply, getInterest, getDeposit); console.log(calculator(1000)) // Returns 20, which is 0.02 * 1000 })();
上面的實現不是純粹的compose實現,因為它不僅將先前的函數結果傳遞給下一個函數,而且還將所有先前的函數結果傳遞給下一個。 這是不擾民,並打開門更復雜的功能,但如果你想更堅持原來的想法撰寫 ,你必須解決的一個問題:
由於您希望鏈中有一個僅返回費率的函數,因此鏈中的下一個函數將僅獲取費率-別無其他。 僅憑一條信息,當然不可能計算結果,結果也需要該值作為輸入。
您可以通過讓getInterest返回一個對象來“解決”此問題,該對象不僅具有速率,而且還具有傳遞給它的值。 您也可以使用數組來實現。
這是一個對象實現:
(()=>{ // Straightforward implementation: const compose = (...fns) => (...args) => { return fns.reduceRight((acc,fn)=>{ return fn(acc); }, args); }; // Return an object with two properties: value & interest const getInterest = (value) => ({ value, interest: value < 1000 ? 0.01 : value < 10000 ? 0.02 : 0.03 }); // Expect an object as argument, with two properties: const getInterestAmount = ({value, interest}) => value * interest; const getDeposit = (value) => value; // Be aware the the rightmost function is executed first: const calculator = compose(getInterestAmount, getInterest, getDeposit); console.log(calculator(1000)) // Returns 20, which is 0.02 * 1000 })();
使用這種方法,您可以傳遞具有更多屬性的對象,因此一切皆有可能。
我實際上很喜歡您的簡單compose函數(僅適用於一元函數),並且我認為您也可以通過以下更改使其暫時起作用:
getInterest
重命名為...Rate
,因為它返回一個值的乘數。 getInterest
函數,該函數采用“ rate getter”和“ value”(采用咖喱形式): getRate => x => getRate(x) * x
在compose
交換計算器參數的順序
我認為compose
通常從右到左( f => g => x => f(g(x))
)起作用, pipe
從左到右( f => g => x => g(f(x))
)
(()=>{ // Generic compose function to handle anything const compose = (...fns) => (x) => { return fns.reduceRight((acc,fn)=>{ return fn(acc); }, x) }; const defaultRate = (value) => { if (value < 1000) { return 1 / 100 } if (value < 10000) { return 2 / 100 } return 3 / 100; }; const getInterest = getRate => x => getRate(x) * x; const getDeposit = x => 1000; const calculator = compose(getInterest(defaultRate), getDeposit); console.log(calculator()); })();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.