在这种情况下,这与JavaScript中的函数式编程有关,在我的示例中,我将使用Ramda。
虽然每个人都完全接受函数式编程,但是围绕如何“正确”进行编程也有很多讨论。
这两个函数将执行完全相同的操作:获取一个列表并返回一个新列表,其中所有字符串均已被修剪。
// data-centric style
const trimList = list => R.map(R.trim, list);
// point-free style
const trimList = R.map(R.trim);
到目前为止,一切都很好。但是,在一个更复杂的示例中,两种样式之间的区别是惊人的:获取一个列表并返回一个新列表,其中所有字符串都等于对象中找到的属性。
var opts = {a: 'foo', b: 'bar', c: 'baz'};
var list = ['foo', 'foo', 'bar', 'foo', 'baz', 'bar'];
myFilter(opts, 'a', list); //=> ["foo", "foo", "foo"]
myFilter(opts, 'b', list); //=> ["bar", "bar"]
// data-centric style
const myFilter = (opts, key, list) => {
var predicate = R.equals(opts[key]);
return R.filter(predicate, list);
};
// point-free style
const myFilter = R.converge(
R.filter, [
R.converge(
R.compose(R.equals, R.prop), [
R.nthArg(1),
R.nthArg(0)]),
R.nthArg(2)]);
除了可读性和个人品味之外,是否有可靠的证据表明一种风格在某些情况下比另一种更合适?
答案 0 :(得分:5)
学术术语是 eta转化。当您具有带有冗余lambda抽象的函数时,例如
const trim = s => s.trim();
const map = f => xs => xs.map(x => f(x));
const trimList = xs => map(trim) (xs); // lambda redundancy
您只需通过eta简化即可去除最后的lamdba抽象:
const trimList = map(trim);
广泛使用 eta缩减时,最终会出现无点样式。但是,这两种版本在功能范式上都很好。只是样式问题。
实际上,至少有两个原因在Javascript中使用 eta抽象(与 eta还原相反):
map = f => xs => xs.map(x => f(x))
所做的那样recur = f => x => f(recur(f)) (x)
中那样立即评估表达式/语句(懒惰的评估效果)答案 1 :(得分:2)
我不知道有证据显示一种风格比另一种风格更具优势。但是,在编程史上,向更高的抽象趋向于明显……而对这种趋势的抵制也同样清晰。从Assembly迁移到Fortran或LISP就是在抽象堆栈上移动。使用SQL而不是定制B树检索是另一回事。在我看来,无论是在Javascript之类的语言中还是在不断变化的编程语言环境中,向FP的迁移都是类似的举动。
但是,其中大部分与比该句法决策更基本的元素有关:方程式推理意味着我们可以在更坚实的基础上构建自己的抽象。因此,纯度和不变性至关重要。没有积分只是很高兴。
也就是说,它通常更简单。这很重要。更简单的代码更易于阅读,更易于修改。请注意,我区分了简单和简单-经典talk by Rich Hickey明确表达了这一区别。那些新风格的人通常会觉得更混乱;厌恶下一代语言及其所有同类语言的汇编程序员也是如此。
通过不定义中间变量,甚至不指定可以推断的参数,我们可以显着提高简单性。
很难争辩:
const foo = (arg) => {
const qux = baz(arg)
return bar(qux)
}
甚至是这个:
const foo = (arg) => bar(baz(arg))
比这简单:
const foo = compose(bar, baz)
那是因为尽管所有三个都涉及这些概念:
第二个也添加:
第一个版本具有:
return
语句而第三个仅添加
如果简单意味着缠结的概念更少,那么即使某些人不太熟悉,无点版本也会更简单。
最后,大部分归结为可读性。与编写代码相比,您花费更多的时间阅读自己的代码。其他人会花很多时间 来阅读它。如果您编写的代码简单易读,那么每个人的体验都会更好。因此,在更容易理解无点代码的地方,请使用它。
但是,不必为了消除一个点而删除每个点。很容易陷入试图使所有内容变得毫无意义的陷阱,因为您可以做到。我们已经知道这是可能的。我们不需要查看血腥细节。
答案 2 :(得分:1)
有一些很好的答案,我认为,将两种样式混合在一起是可行的方法。
最后一个无点样式示例有点混乱,您可以减少混乱:
const myFilter = converge(
filter,
[compose(equals , flip(prop)) , nthArg(2)]
)