Sco*_*yet 13 javascript functional-programming ramda.js
我已经在一个名为Ramda的Javascript FP库上工作了一段时间,我在命名方面遇到了一些问题.(你听说过这条旧线,对吗?"计算机科学只有两个难题:缓存失效,命名事物和一个一个错误.")
在这个库中,(几乎)多个参数的每个函数都会自动计算.这适用于大多数用例.但是有一些问题是一些函数是非交换二元运算符.问题在于,英语名称往往意味着与应用currying时所发生的不同.例如,
var div10 = divide(10);
Run Code Online (Sandbox Code Playgroud)
听起来它应该是一个将其参数除以10的函数.但实际上它将其参数分为 10,如果你看一下这个定义就很清楚了:
var divide = curry(function(a, b) {
return a / b;
});
Run Code Online (Sandbox Code Playgroud)
所以相反的预期:
div10(50); //=> 5 // NO!!
Run Code Online (Sandbox Code Playgroud)
事实上,你得到了
div10(50); //=> 0.2 // Correct, but surprising!
Run Code Online (Sandbox Code Playgroud)
我们通过记录从人们的预期可能的差别,并创建处理这个问题divideBy,这只是flip(divide)和subtractN,这是flip(subtract).但是我们还没有找到一个很好的等价函数,例如lt:
R.lt = curry(function(a, b) {
return a < b;
});
Run Code Online (Sandbox Code Playgroud)
或它的表兄弟lte,gt和gte.
我自己的直觉就是这样
map(lt(5), [8, 6, 7, 5, 3, 0, 9]);
//=> [false, false, false, false, true, true, false]
Run Code Online (Sandbox Code Playgroud)
但当然,它实际上会回归
//=> [true, true, true, false, false, false, true]
Run Code Online (Sandbox Code Playgroud)
所以我想为它的同类做同样的文档和点到备用名称例程lt.但我找不到一个好名字.唯一真正的候选人已经ltVal和使用两个参数调用时并不真正起作用.我们确实讨论了这个问题,但没有得出好的结论.
让其他人处理这个并提出好的解决方案吗?或者即使没有,对这些功能的翻转版本的名称有什么好的建议吗?
更新
有人认为,这可以被关闭,因为"不清楚你问什么,我想真正的问题是在解释减轻了一些.简单的问题是:
对于翻转版本,什么是好的,直观的名称lt?
Aad*_*hah 26
首先,您要维护的函数式编程库是值得称道的.我一直想写一个,但我从来没有找到时间这样做.
考虑到您正在编写函数式编程库,我将假设您了解Haskell.在Haskell中,我们有函数和运算符.函数始终是前缀.运算符总是中缀.
Haskell中的函数可以使用反引号转换为运算符.例如div 6 3可以写成6 `div` 3.类似地,可以使用括号将运算符转换为函数.例如2 < 3可以写成(<) 2 3.
也可以使用部分部分应用运算符.有两种类型的部分:左侧部分(例如(2 <)和(6 `div`))和右侧部分(例如(< 3)和(`div` 3)).左侧部分翻译如下:(2 <)变为(<) 2.正确的部分:(< 3)成为flip (<) 3.
在JavaScript中我们只有函数.在JavaScript中创建运算符没有"好"的方法.你可以编写像这样的代码(2).lt(3),但我认为这是粗鲁的,我强烈建议不要编写这样的代码.
因此,我们可以将普通函数和运算符编写为函数:
div(6, 3) // normal function: div 6 3
lt(2, 3) // operator as a function: (<) 2 3
Run Code Online (Sandbox Code Playgroud)
在JavaScript中编写和实现中缀运算符是一件痛苦的事.因此,我们不会有以下内容:
(6).div(3) // function as an operator: 6 `div` 3
(2).lt(3) // normal operator: 2 < 3
Run Code Online (Sandbox Code Playgroud)
部分很重要.让我们从正确的部分开始:
div(3) // right section: (`div` 3)
lt(3) // right section: (< 3)
Run Code Online (Sandbox Code Playgroud)
当我看到div(3)我希望它是一个正确的部分(即它应该表现为(`div` 3)).因此,根据最不惊讶的原则,这是应该实施的方式.
现在出现左侧部分的问题.如果div(3)是正确的部分那么左侧部分应该是什么样的?我认为它应该是这样的:
div(6, _) // left section: (6 `div`)
lt(2, _) // left section: (2 <)
Run Code Online (Sandbox Code Playgroud)
对我来说,这就是"以某种方式划分6"和"比某种东西小2"?我更喜欢这种方式,因为它是明确的.根据Python的禅宗,"明确比隐含更好".
那么这对现有代码有何影响?例如,考虑一下这个filter功能.要过滤列表中的奇数,我们会写filter(odd, list).对于这样的功能,是否按预期进行了工作?例如,我们如何编写filterOdd函数?
var filterOdd = filter(odd); // expected solution
var filterOdd = filter(odd, _); // left section, astonished?
Run Code Online (Sandbox Code Playgroud)
根据最不惊讶的原则,它应该是简单的filter(odd).该filter功能并不意味着用作操作员.因此,程序员不应该被迫将其用作左侧部分.功能和"功能操作员"之间应该有明确的区别.
幸运的是,区分函数和函数操作符非常直观.例如,该filter函数显然不是函数运算符:
filter odd list -- filter the odd numbers from the list; makes sense
odd `filter` list -- odd filter of list? huh?
Run Code Online (Sandbox Code Playgroud)
另一方面,该elem函数显然是一个函数运算符:
list `elem` n -- element n of the list; makes sense
elem list n -- element list, n? huh?
Run Code Online (Sandbox Code Playgroud)
重要的是要注意,这种区别是唯一可能的,因为函数和函数运算符是互斥的.理所当然,给定一个函数,它可以是普通函数,也可以是函数运算符,但不是两者.
有趣的是,如果你flip的参数给定一个二元函数,那么它就变成二元运算符,反之亦然.例如,考虑的翻转变形filter和elem:
list `filter` odd -- now filter makes sense an an operator
elem n list -- now elem makes sense as a function
Run Code Online (Sandbox Code Playgroud)
事实上,对于n大于1的任何n-arity函数,这可以推广.你会看到,每个函数都有一个主要参数.平凡地说,对于一元函数来说,这种区别是无关紧要的.然而,对于非一元函数,这种区别很重要.
filter odd list,where list是主要参数).在列表末尾有主要参数是函数组合所必需的.list `elem` n,哪个list是主要参数).list `elem` n将list.elem(n)在OOP中编写.OOP中的链接方法类似于FP [1]中的函数组合链.filter odd list干是filter odd.在list `elem` n干是(`elem` n).odd `filter` list,elem list n没有任何意义.然而list `filter` odd,elem n list因为茎不变,所以有意义.回到主题,由于函数和函数运算符是互斥的,因此您可以简单地处理函数运算符,而不是处理正常函数的方式.
我们希望运算符具有以下行为:
div(6, 3) // normal operator: 6 `div` 3
div(6, _) // left section: (6 `div`)
div(3) // right section: (`div` 3)
Run Code Online (Sandbox Code Playgroud)
我们想要定义运算符如下:
var div = op(function (a, b) {
return a / b;
});
Run Code Online (Sandbox Code Playgroud)
op功能的定义很简单:
function op(f) {
var length = f.length, _; // we want underscore to be undefined
if (length < 2) throw new Error("Expected binary function.");
var left = R.curry(f), right = R.curry(R.flip(f));
return function (a, b) {
switch (arguments.length) {
case 0: throw new Error("No arguments.");
case 1: return right(a);
case 2: if (b === _) return left(a);
default: return left.apply(null, arguments);
}
};
}
Run Code Online (Sandbox Code Playgroud)
该op函数类似于使用反引号将函数转换为Haskell中的运算符.因此,您可以将其添加为Ramda的标准库函数.在文档中还要提到运算符的主要参数应该是第一个参数(即它应该看起来像OOP,而不是FP).
[1]在旁注中,如果Ramda允许您编写函数,就好像它是在常规JavaScript中链接方法(例如foo(a, b).bar(c)代替compose(bar(c), foo(a, b))),那将是非常棒的.这很难,但可行.
| 归档时间: |
|
| 查看次数: |
2660 次 |
| 最近记录: |