在 Ramda 中做一些无意义的事情

Making something point-free in Ramda

我现在正在学习 JS 库 Ramda。似乎有一种模式我无法正确解决。例如我有这个代码。

const filterPayments = filter => payments =>
  r.filter(
    r.allPass([
      typePred(filter),
      amountPred(filter),
      accountPred(filter),
      currencyPred(filter)
    ]),
    payments
  );

现在这似乎是我离能够使它成为免费点不远了。我似乎无法找到正确的方法。

此外,我很难使像这样的逻辑函数完全起作用且无意义:

const amountPred = filter => p =>
  r.and(p.betrag >= filter.amount.min, p.betrag <= filter.amount.max);

也许有人可以指出正确的方向或提供一些指导?

我提供的指导是坚持你目前拥有的:)

我可以向您展示一个示例,说明您所拥有的内容的无点翻译可能是什么样子,尽管生成的代码读起来非常可怕,而且实际上只对心理体操有用。

首先,payments出现在函数表达式两边的最后位置,所以我们可以简单地删除它。

const filterPayments = filter =>
  R.filter(
    R.allPass([
      typePred(filter),
      amountPred(filter),
      accountPred(filter),
      currencyPred(filter)
    ]));

接下来我们可以查看处理传递给R.allPass

的函数列表
R.allPass(R.map(p => p(filter), [typePred, amountPred, accountPred, currencyPred]))

现在我们可以开始删除 filter 参数,方法是翻转 R.map 将其推向末尾。

R.allPass(R.flip(R.map)([typePred, amountPred, accountPred, currencyPred])(p => p(filter))

现在让 filter 和参数 p => p(filter) 而不是关闭它,使它成为:

R.allPass(R.flip(R.map)
  ([typePred, amountPred, accountPred, currencyPred])
  (R.applyTo(filter)))

现在开始形成像 h(g(f(a))) 的形状,我们可以使用 R.pipeR.composeR.o 等将其转换为无点

const filterPayments = R.compose(
  R.filter,
  R.allPass,
  R.flip(R.map)([typePred, amountPred__, accountPred, currencyPred]),
  R.unary(R.applyTo) // R.unary is needed here to tell Ramda only to expect the first argument in this composition
)

这为我们提供了一个函数,该函数现在应该等同于无点形式的 filterPayments

你的 amountPred 例子变得更加复杂,我想你之后会同意你已经拥有的是两者之间更好的选择。

同样,我们首先尝试将参数推到表达式每一边的末尾:

filter => p =>
  both(
    x => x >= filter.amount.min,
    x => x <= filter.amount.max
  )(p.betrag)

然后我们可以删除 p:

filter => R.o(
  both(
    x => x >= filter.amount.min,
    x => x <= filter.amount.max
  ),
  prop('betrag'))

传递给both的函数也可以换出:

filter => R.o(
  both(
    R.flip(R.gte)(filter.amount.min),
    R.flip(R.lte)(filter.amount.max)
  ),
  prop('betrag'))

现在要解决 filter,我们可以使用 R.converge 将相同的参数传递给多个函数,然后将每个函数的结果组合在一起。

filter => R.o(
  R.converge(both, [
    f => R.flip(R.gte)(R.path(['amount', 'min'], f),
    f => R.flip(R.lte)(R.path(['amount', 'max'], f)
  ])(filter),
  prop('betrag'))

而现在f在最后的位置,所以可以通过组合删除它:

filter => R.o(
  R.converge(both, [
    R.o(R.flip(R.gte), R.path(['amount', 'min'])),
    R.o(R.flip(R.lte), R.path(['amount', 'max']))
  ])(filter),
  prop('betrag'))

filter 写到最后会变得非常混乱,涉及翻转组合函数。

filter => R.o(
  R.flip(R.o)(R.prop('betrag')),
  R.converge(both, [
    R.o(R.flip(R.gte), R.path(['amount', 'min'])),
    R.o(R.flip(R.lte), R.path(['amount', 'max']))
  ])
)(filter)

最后...

const amountPred = R.o(
  R.flip(R.o)(R.prop('betrag')),
  R.converge(both, [
    R.o(R.flip(R.gte), R.path(['amount', 'min'])),
    R.o(R.flip(R.lte), R.path(['amount', 'max']))
  ])
)

... 将其与您的原文进行比较,我知道我更喜欢阅读哪一个:

const amountPred = filter => p =>
  p.betrag >= filter.amount.min && p.betrag <= filter.amount.max

我已经开始做与 Scott Christopher 所做的相同类型的工作,并将主要功能重构为 point-free,从一开始就知道我的建议几乎肯定是不要为 point-free 而烦恼这个。我找到了我的免积分版本,它比我预期的要简单。在开始谓词之前,我查看了 Scott 的解决方案。

他做了很多我会做的事情,而且可能比我做的更好。当然,他以类似的建议结尾。我总是试图建议人们不要迷恋无意义。当它使您的代码更清晰、更易于理解时使用它。不用的时候不要用。

我根本不会回答,除了我最终得到了一个更好的 main 函数的无点版本,并且想提供它以供考虑:

const filterPayments = pipe(
  juxt([typePred, amountPred, accountPred, currencyPred]),
  allPass,
  filter
)

虽然这本书相当干净,但我认为它的可读性不如您的原文,因此我不推荐它。但是如果你真的想要免分,这个是有可能的。