有没有办法在不列出参数的情况下编写这个 Javascript 函数?

Is there a way to write this Javascript function without listing the arguments?

我正在尝试编写一个函数,使用另一个函数比较两个项目,然后检查结果是否大于也提供给函数的其他值。我可以这样写:

const compareDifference = function(t1, t2, threshold) {
    return getDifference(t1, t2) > threshold;
};

...但这似乎不太实用。我为经典组合找到的每个示例都假定我会在调用函数之前知道要比较的值,在这种情况下我可以像这样在功能上编写它:

const greaterThan = (x, y) => x > y;
const greaterThan10 = _.partial(greaterThan, _, 10);
const compareDifference = _.compose(greaterThan10, getDifference);

由于我是函数式编程的新手,我觉得我在这里缺少一些简单或基础的东西。有没有一种方法可以编写函数,以便它包含要传递给 greaterThan 的参数,而无需我明确提及它?理想情况下,它应该是这样的:

const compareDifference = _.compose(_.partial(greaterThan, _), getDifference);

如果我用这个树错误的树,然后告诉我,我会编辑,但如果我想做这样的事情 'more functional' 我会做以下事情:

let greaterThan = _.curry((x, y) => y > x); // notice the args are flipped
let difference = _.curry((x, y) => Math.abs(x - y));
let greaterThan5 = greaterThan(5); // this naming is why we ordered the args backwards
let differenceBetweenGreaterThan5 = _.compose(greaterThan5, difference);
differenceBetweenGreaterThan5(10, 34); // true
differenceBetweenGreaterThan5(10, 6); // false

然后我们可以像这样重写您的原始函数:

let compareDiff = (threshold, x, y) => {
  return _.compose(greaterThan(threshold), difference)(x)(y);
};

虽然我可能只会使用 differenceBetweenGreaterThan5

另外,对于长得可笑的变量名,我深表歉意,但我希望我的命名非常清楚。还有一些其他事项需要注意:我将参数重新排序为 greaterThan 以使部分应用程序的命名更加合理并避免需要 _ 占位符。虽然我柯里化 difference 并认为它对通用的东西有好处,但对于这个例子来说并不是绝对必要的。

至于我认为你遗漏了什么,在这种情况下的功能方法(根据我对 'functional approach' 含义的理解)是我们打破了获得两个数字之间的差异并查看是否第三个落在该范围内,将其分解为原子成分,并将其构造为像 greaterThandifference.

这样的原子元素的组合

它的破坏和重建是困难的:为了灵活性、方便性和清晰性,彻底地这样做需要重新排列参数(即使相对于我在上面段落中拼写的英文版本,因为我给出 'third'编号在前)。在我看来,参数和片段重新排序部分正是您所缺少的。

我认为 LUH3417 的回答对初学者来说很棒。它涉及一些基础知识,但我认为还有一些其他信息的空间

首先,如果您想要与原始问题完全相同的 API,您可以将其分解成这样的部分。

const comp = f=> g=> x=> f (g (x))
const comp2 = comp (comp) (comp)
const flip = f=> x=> y=> f (y) (x)
const sub = x=> y=> y - x
const abs = x=> Math.abs
const diff = comp2 (Math.abs) (sub)
const gt = x=> y=> y > x

// your function ...
// compose greaterThan with difference
// compareDifference :: Number -> Number -> Number -> bool
const compareDifference = comp2 (flip(gt)) (diff)

console.log(compareDifference (3) (1) (10))
// = gt (10) (abs (sub (1) (3)))
// = Math.abs(1 - 3) > 10
// => false

console.log(compareDifference (5) (17) (10))
// = gt (10) (abs (sub (5) (17)))
// = Math.abs(17 - 5) > 10
// => true

但是,您怀疑您的原始代码感觉不到该功能是对的。我在这里给你的代码有效,但它仍然感觉...... off,对吧?我认为如果将它设为 higher-order function,即接受一个函数作为参数的函数(and/or returns 一个函数),我认为可以极大地改进您的函数。


黄砖路

然后我们可以创建一个名为 testDifference 的通用函数,该函数将阈值函数 t 作为输入和 2 数字以将阈值计算基于

// testDifference :: (Number -> bool) -> Number -> Number -> bool
const testDifference = t=> comp2 (t) (diff)

看一下实现,很有道理。为了测试差异,我们需要一个测试(一些函数),我们需要两个计算差异[=73的数字=].

这是一个使用它的例子

testDifference (gt(10)) (1) (3)
// = gt (10) (abs (sub (1) (3)))
// = Math.abs(1 - 3) > 10
// = Math.abs(-2) > 10
// = 2 > 10
// => false

这是一个很大的改进,因为 >(或 gt)不再硬编码在您的函数中。这使它更加通用。看,我们可以像 lt

一样轻松地使用它
const lt = x=> y=> y < x

testDifference (lt(4)) (6) (5)
// = lt (4) (abs (sub (6) (5)))
// = Math.abs(5 - 6) < 4
// = Math.abs(-1) < 4
// = 1 < 4
// => true

或者让我们定义一个非常严格的阈值,强制数字具有 1

精确差异
const eq = x=> y=> y === x
const mustBeOne = eq(1)

testDifference (mustBeOne) (6) (5)
// = eq (1) (abs (sub (6) (5)))
// = Math.abs(5 - 6) === 1
// = Math.abs(-1) === 1
// = 1 === 1
// => true

testDifference (mustBeOne) (5) (8)
// = eq (1) (abs (sub (5) (8)))
// = Math.abs(8 - 5) === 1
// = Math.abs(3) === 1
// = 3 === 1
// => false

因为 testDifference 是柯里化的,所以你也可以将它用作部分应用函数

// return true if two numbers are almost the same
let almostSame = testDifference (lt(0.01))

almostSame (5.04) (5.06)
// = lt (0.01) (abs (sub (5.04) (5.06)))
// = Math.abs(5.06 - 5.04) < 0.01
// = Math.abs(0.02) < 0.01
// = 0.02 < 0.01
// => false

almostSame (3.141) (3.14)
// = lt (0.01) (abs (sub (3.141) (3.14)))
// = Math.abs(3.14 - 3.141) < 0.01
// = Math.abs(-0.001) < 0.01
// = 0.001 < 0.01
// => true

现在一起

这是实现了 testDifference 的代码片段,您可以 运行 在浏览器中查看它的运行情况

// comp :: (b -> c) -> (a -> b) -> (a -> c)
const comp = f=> g=> x=> f (g (x))

// comp2 :: (c -> d) -> (a -> b -> c) -> (a -> b -> d)
const comp2 = comp (comp) (comp)

// sub :: Number -> Number -> Number
const sub = x=> y=> y - x

// abs :: Number -> Number
const abs = x=> Math.abs

// diff :: Number -> Number -> Number
const diff = comp2 (Math.abs) (sub)

// gt :: Number -> Number -> bool
const gt = x=> y=> y > x

// lt :: Number -> Number -> bool
const lt = x=> y=> y < x

// eq :: a -> a -> bool
const eq = x=> y=> y === x

// (Number -> bool) -> Number -> Number -> bool
const testDifference = f=> comp2 (f) (diff)

console.log('testDifference gt', testDifference (gt(10)) (1) (3))
console.log('testDifference lt', testDifference (lt(4)) (6) (5))
console.log('testDifference eq', testDifference (eq(1)) (6) (5))

// almostSame :: Number -> Number -> bool
let almostSame = testDifference (lt(0.01))

console.log('almostSame', almostSame (5.04) (5.06))
console.log('almostSame', almostSame (3.141) (3.14))