3 回答

TA贡献1821条经验 获得超4个赞
第二种方法更为惯用。事实上,第二种方法在函数式编程中有一个名字。将共享静态值作为输入(又称为环境)的函数称为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})
在这个例子中,我们可以看到功能级抽象是如何与意图相关的。
永远记住牢记“降职规则”。
现在让我们改变应用意图。现在我们正在向法律机构申请,我们必须进行复杂的操作来申请一些税收。
现在 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
添加回答
举报