javascript – “lt”,“lte”,“gt”和“gte”的翻转版本的好名称?

我已经在一个名为 Ramda的Javascript FP库上工作了一段时间,命名的时候我有一个小问题. (你听说过旧的行,对吧?“计算机科学中只有两个难题:缓存失效,命名事物和逐个错误.”)

在这个库中,(几乎)每个功能的多个参数都是自动咖喱.这对大多数用例来说都很好.但是有一些问题是一些非交换二元运算符的函数.问题在于,英文名称往往暗示与应用咖喱时会发生什么不同的东西.例如,

var div10 = divide(10);

听起来应该是一个将其参数除以10的函数.但是实际上它将其参数分为10,如果您查看定义,这是很清楚的:

var divide = curry(function(a, b) {
    return a / b;
});

所以反而预期:

div10(50); //=> 5 // NO!!

其实你得到了

div10(50); //=> 0.2 // Correct, but surprising!

我们通过记录与人们可能的期望的差异来处理这个问题,并创建了分割,它只是翻转(除法)和减法N,即翻转(减法).但是我们还没有找到一个很好的等价物,例如:

R.lt = curry(function(a, b) { 
    return a < b;
});

或其表亲lte,gt和gte.

我自己的直觉就是这样

map(lt(5), [8, 6, 7, 5, 3, 0, 9]); 
//=> [false, false, false, false, true, true, false]

但当然,它实际上是返回的

//=> [true, true, true, false, false, false, true]

所以我想为lt和它的ilk做同样的文件和点到替代名称例程.但我没有找到一个好名字.唯一真正的候选人是ltVal,并且在两个参数调用时都不会真正工作.我们做了discuss this issue,但没有得到很好的结论.

有人处理这件事,拿出好的解决方案吗?或者即使不是,这些功能的翻转版本的名称有什么好的建议?

更新

有人建议,这是关闭的,因为“你不清楚你在问什么”,我猜这个问题真的在解释中失去了一点.简单的问题是:

什么是一个好的,直观的名称为翻转版本的?

首先,你正在维护的功能性编程库上的赞美.我一直想自己写一个,但是我从来没有找到时间去做.

考虑到你正在编写一个函数式编程库,我将假设你知道Haskell.在Haskell,我们有功能和操作.功能总是前缀.运算符始终是中缀.

Haskell中的函数可以使用反引号转换为运算符.例如,div 6 3可以写成6`div` 3.类似的操作符可以使用括号转换为函数.例如, 3可以写成(<)2 3. 运算符也可以使用部分部分应用.有两种类型的部分:左部分(例如(2′)和(6`div`))和右部分(例如(3)和(`div` 3)).左部分被翻译如下:(2 <)变成(<)2.右部分:(< 3)变成翻转(<)3. 在JavaScript中,我们只有功能.在JavaScript中创建运算符没有“好”的方式.你可以编写代码(2).lt(3),但是在我谦虚的意见中,这是非常的,我强烈建议不要编写这样的代码. 所以我们可以写正常的函数和运算符作为函数:

div(6, 3) // normal function: div 6 3
lt(2, 3) // operator as a function: (<) 2 3

在JavaScript中编写和实现中缀运算符是一件很痛苦的事情.所以我们不会有以下几点:

(6).div(3) // function as an operator: 6 `div` 3
(2).lt(3) // normal operator: 2 < 3

但部分重要.我们从正确的部分开始:

div(3) // right section: (`div` 3)
lt(3) // right section: (< 3)

当我看到div(3),我希望它是一个正确的部分(即它应该表现为(`div` 3)).因此,根据principle of least astonishment,这是应该实施的方式.

现在是左边的问题.如果div(3)是一个正确的部分,那么左部分是什么样的?在我的谦虚的意见中,它应该是这样的:

div(6, _) // left section: (6 `div`)
lt(2, _) // left section: (2 <)

对我来说,这是“分6”,“比事物少2”,我喜欢这样,因为它是明确的.根据The Zen of Python,“明确胜于隐”.

那么这会影响现有的代码呢?例如,考虑过滤功能.要过滤列表中的奇数,我们将写入过滤器(奇数,列表).这样的功能是否按预期进行工作?例如,我们如何编写一个filterOdd函数?

var filterOdd = filter(odd);    // expected solution
var filterOdd = filter(odd, _); // left section, astonished?

根据最不惊讶的原则,应该只是过滤(奇数).过滤器功能不用作运算符.因此,程序员不应该被强制用作左侧部分.函数和函数运算符之间应该有明确的区别.

幸运的是区分函数和函数运算符是非常直观的.例如,过滤器函数显然不是函数运算符:

filter odd list -- filter the odd numbers from the list; makes sense
odd `filter` list -- odd filter of list? huh?

另一方面,elem函数显然是一个函数运算符:

list `elem` n -- element n of the list; makes sense
elem list n -- element list, n? huh?

重要的是要注意,这种区别是可能的,因为函数和函数运算符是相互排斥的.这是理由,给定一个函数,它可能是一个正常的函数,或者一个函数运算符,但不是两个.

有趣的是注意到,如果你翻转其参数,给定一个二进制函数,那么它将成为一个二进制运算符,反之亦然.例如考虑过滤器和elem的翻转变体:

list `filter` odd -- now filter makes sense an an operator
elem n list -- now elem makes sense as a function

实际上,这可以推广为n大于1的任何n-arity函数.你看到,每个函数都有一个主要参数.简而言之,对于一元功能,这种区别是无关紧要的.然而,对于非一元函数,这个区别很重要.

>如果函数的主参数在参数列表的末尾,则函数是一个正常的函数(例如,过滤器奇数列表,其中list是主要参数).列表末尾的主要参数对于功能组合是必要的.
>如果函数的主参数在参数列表的开始处,则该函数是一个函数运算符(例如,列表“elem”,其中list是主要参数).
>运算符类似于OOP中的方法,主参数类似于方法的对象.例如,列表“elem”n将在OOP中写成list.elem(n). OOP中的链接方法类似于FP [1]中的功能组成链.
>函数的主参数可能只是在参数列表的开头或末尾.它在任何地方都没有意义.这个属性对于二进制函数是真的.因此,翻转二进制函数使它们成为运算符,反之亦然.
>其余的参数和函数形成一个不可分割的原子,称为参数列表的句柄.例如,在过滤器奇数列表中,杆是过滤器奇数.在列表“elem”中,句柄是(`elem` n).
>句子的顺序和元素必须保持不变,以使表达式有意义.这就是为什么奇怪的“过滤器”列表和元素列表n没有任何意义.然而,列表“过滤器”奇数和元素列表是有道理的,因为干是不变的.

回到主题,由于函数和函数运算符是互斥的,您可以简单地对待函数运算符,而不是处理正常函数的方式.

我们希望运算符具有以下行为:

div(6, 3) // normal operator: 6 `div` 3
div(6, _) // left section: (6 `div`)
div(3)    // right section: (`div` 3)

我们要定义运算符如下:

var div = op(function (a, b) {
    return a / b;
});

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);
        }
    };
}

op函数类似于使用反引号将函数转换为Haskell中的运算符.因此,您可以将其添加为Ramda的标准库函数.在文档中也提到操作符的主要参数应该是第一个参数(即它应该看起来像OOP而不是FP).

[1]在一个侧面的注释,如果Ramda允许你组合函数,就像在正常JavaScript中的链接方法(例如foo(a,b).bar(c)而不是compose(bar(c),foo (a,b))).这很难,但可行.

相关文章
相关标签/搜索