3 回答

TA貢獻1821條經驗 獲得超5個贊
第二種方法更為慣用。事實上,第二種方法在函數式編程中有一個名字。將共享靜態值作為輸入(又稱為環境)的函數稱為reader。
// Reader e a = e -> a
// ask : Reader e e
const ask = x => x;
// pure : a -> Reader e a
const pure = x => _ => x;
// bind : Reader e a -> (a -> Reader e b) -> Reader e b
const bind = f => g => x => g(f(x))(x);
// reader : Generator (Reader e a) -> Reader e a
const reader = gen => (function next(data) {
? ? const { value, done } = gen.next(data);
? ? return done ? value : bind(value)(next);
}(undefined));
// Environment = { foo : Number, bar : Number }
// function1 : Reader Environment Number
const function1 = reader(function* () {
? ? const { foo, bar } = yield ask;
? ? return pure(foo + bar);
}());
// function2 : Reader Environment Number
const function2 = reader(function* () {
? ? const { foo, bar } = yield ask;
? ? return pure(foo * bar);
}());
// myFunction : Reader Environment (Array Number)
const myFunction = reader(function* () {
? ? const res1 = yield function1;
? ? const res2 = yield function2;
? ? return pure([res1, res2]);
}());
// results : Array Number
const results = myFunction({ foo: 10, bar: 20 });
console.log(results);
在上面的示例中,我們使用一元表示法定義function1
、function2
、 和。myFunction
請注意,myFunction
沒有明確將環境作為輸入。它也沒有顯式地將環境傳遞給function1
和function2
。所有這些“管道”都是由pure
和bind
函數處理的。我們使用一元動作訪問一元上下文中的環境ask
。
Reader
然而,當我們使用 monad 轉換器將 monad 與其他 monad結合起來時,真正的優勢就會出現ReaderT
。
編輯:如果您不想,則不必使用單子表示法。您可以按如下方式定義function1
、function2
、 和。myFunction
// Reader e a = e -> a
// Environment = { foo : Number, bar : Number }
// function1 : Reader Environment Number
const function1 = ({ foo, bar }) => foo + bar;
// function2 : Reader Environment Number
const function2 = ({ foo, bar }) => foo * bar;
// myFunction : Reader Environment (Array Number)
const myFunction = env => {
? ? const res1 = function1(env);
? ? const res2 = function2(env);
? ? return [res1, res2];
};
// results : Array Number
const results = myFunction({ foo: 10, bar: 20 });
console.log(results);
缺點是現在您明確地將環境作為輸入并將環境傳遞給子計算。不過,這可能是可以接受的。
編輯:這是另一種編寫方法,不使用一元符號,但仍然使用ask、pure和bind。
// Reader e a = e -> a
// ask : Reader e e
const ask = x => x;
// pure : a -> Reader e a
const pure = x => _ => x;
// bind : Reader e a -> (a -> Reader e b) -> Reader e b
const bind = f => g => x => g(f(x))(x);
// Environment = { foo : Number, bar : Number }
// function1 : Reader Environment Number
const function1 = bind(ask)(({ foo, bar }) => pure(foo + bar));
// function2 : Reader Environment Number
const function2 = bind(ask)(({ foo, bar }) => pure(foo * bar));
// myFunction : Reader Environment (Array Number)
const myFunction =
? ? bind(function1)(res1 =>
? ? ? ? bind(function2)(res2 =>
? ? ? ? ? ? pure([res1, res2])));
// results : Array Number
const results = myFunction({ foo: 10, bar: 20 });
console.log(results);
請注意,使用生成器的一元表示法只是上述代碼的語法糖。

TA貢獻1826條經驗 獲得超6個贊
您的兩種方法都是正確的。這里的問題是該代碼與應用程序相關的上下文,這些函數是否與它們定義/使用的范圍相關?
考慮這個例子。
const Calculator = class {
complexOperation({foo, bar}) {
const results = []
const res1 = this.sum({foo, bar});
const res2 = this.dot({foo, bar});
results.push(res1, res2);
return results;
}
sum({foo, bar}) {
return foo + bar;
}
dot({foo, bar}){
return foo * bar;
}
};
var calc = new Calculator();
calc.complexOperation({foo: 2, bar: 3})
在這個例子中,我們可以看到功能級抽象是如何與意圖相關的。
永遠記住牢記“降職規則”。
現在讓我們改變應用意圖?,F在我們正在向法律機構申請,我們必須進行復雜的操作來申請一些稅收。
現在 sum 和點不應該成為類的一部分,因為只會在復雜操作中使用,新開發人員不關心 function1(我將其重命名為 sum)做什么,他們不必閱讀它,所以我們可以改變抽象級別。事實上,您將以包含一些步驟的方法結束。
在其他語言中,例如c#,您可以在使用后定義函數,但在javascript中則不能,因此您不能在JS中的本地函數中應用Stepdown規則。有些人顛倒了Stepdown Rule,將所有局部函數都定義在函數start中,所以他們的眼睛就跳到最后一個局部函數結束括號并開始閱讀。
const BusinessTaxes = class {
complexOperation({foo, bar}) {
const addTax = () => {
return foo + bar;
}
const dotTax = () => {
return foo * bar;
}
// Jump your eyes here
const results = []
const res1 = addTax({foo, bar});
const res2 = dotTax({foo, bar});
results.push(res1, res2);
return results;
};
};
var businessTax= new BusinessTaxes();
businessTaxes.complexOperation({foo: 2, bar: 3})
總之,將您的代碼組織成相同的抽象級別,保持其結構化并與您的決策保持一致,您的代碼將具有可讀性和可維護性。

TA貢獻1817條經驗 獲得超6個贊
當開發人員專注于明確自己的意圖時,可讀性往往是一個副產品。
下一個開發人員(或未來的您)會理解您的意圖嗎?
恕我直言,這是您應該回答的唯一問題嗎,因為它關注的是比“這看起來不錯嗎?”更具體的問題。
從這個角度來看,兩個版本都做到了這一點。
除了兩者:
可以用更好的名字
可以使用新的語法來使其更容易被眼睛看到
const sumproduct_pair = ({a, b}) => {
const sum = () => a + b;
const product = () => a * b;
return [sum(), product()];
};
或者
const sum = ({a, b}) => a + b;
const product = ({a, b}) => a * b;
const sumproduct_pair = ({a, b}) => [sum({a, b}), product({a, b})];
然而,這兩個版本都可以改進,但還是 YMMV:
在第一個版本中, 和sum都不product需要存在。它們顯然不適合重復使用,而且非常簡單,可以簡化為最簡單的表達:
const sumproduct_pair = ({a, b}) => [a+b, a*b];
在第二個版本中,如果您打算重用sum并重product用,請考慮“針對接口而不是實現進行設計”。
該函數sumproduct_pair需要一個具有屬性的對象a,b但這并不意味著所有其他函數都需要具有相同的接口:
const sum = (a, b) => a + b;
const product = (a, b) => a * b;
const sumproduct_pair = ({a, b}) => [sum(a, b), product(a, b)];
雖然這看起來像是一個微不足道的更改,但它刪除了一些不必要的大括號(如果您想通過減少編寫來提高可讀性),最重要的是允許 和 處理sum未知product數量的數字:
const sum = (...xs) => xs.reduce((ret, x) => ret + x, 0);
const product = (...xs) => xs.reduce((ret, x) => ret * x, 1);
sum(1, 2, 3); //=> 6
sum(1, 2, 3, 4); //=> 10
product(1, 2, 3); //=> 6
product(1, 2, 3, 4); //=> 24
添加回答
舉報