【问题标题】:Ramda selfComposeWhileRamda selfComposeWhile
【发布时间】:2021-02-19 04:51:04
【问题描述】:

问题:

我正在学习函数式编程

开个玩笑,但也...

我有一个辅助函数,它一遍又一遍地与自己组合一个函数,直到满足某些条件。像 f(f(f(f(f(f(f(x)))))))compose(f,f,f,f,f,f,f)(x) 一样,除非被告知停止,否则它会继续运行。

按照我实现它的方式,它真的不像是合成(无论如何,这可能是在这里使用的错误词)

这是我目前的解决方案:

const selfComposeWhile = curry(
  (pred, fn, init) => {
    let prevVal = null;
    let nextVal = init;
    while(prevVal == null || pred(prevVal, nextVal)){
      prevVal = nextVal;
      nextVal = fn(nextVal);
    }
    return nextVal;
  }
);

这里正在使用:

const incOrDec = ifElse(gt(30), inc, dec);
console.log(
  selfComposeWhile(lt, incOrDec, 0)
); // -> 29

我不想使用递归,因为 JavaScript 没有适当的尾递归,而且这个网站的同名(堆栈溢出)是我如何使用它的一个真正问题。

它没有任何错误,但我一直在尝试通过将函数式编程技术应用于一个虚拟问题来学习它们,这是我的代码明显脱颖而出的少数几个地方之一势在必行。

我也有

useWith(selfComposeWhile, [pipe(nthArg(1), always)]);

这需要一个只与 nextVal 相关的谓词,这似乎是更一般的情况。

问题:

谁能想到一种更实用(无递归)的方式来编写selfComposeWhile 及其表亲?

【问题讨论】:

  • 请注意与R.until 的相似之处。它不会解决您的问题,因为它采用的谓词是一元的。但是实现是类似的。正如 Ori Drori 所说,unfold 可能是 Ramda 函数中最好的选择,但我不得不说,我希望我们为 unfold 选择不同的 API,例如来自用户 @Thankyou 的 this one
  • @ScottSauyet 我注意到很多名称/命名约定只要稍加思考就可以理解,但我不会想到用它们搜索 Docs/Google。也许是没有数学背景的部分缺点。
  • 我创建了这个库并参与了几乎所有的命名决定,但我仍然有一些无法理解,或者他们的名字并不总是有意义的。 fold/unfold 一听就明白了,until 的名字也很好。但是想出它们,或者理解assocjuxt 需要更多的努力。我不知道任何解决方案。

标签: javascript ramda.js


【解决方案1】:

R.unfold 主要执行您的操作,它接受一个种子值 (init),然后对其进行转换,并在每次迭代时返回当前值和新的种子值。在每次迭代中,您需要决定是继续使用还是停止使用谓词。

你的函数和 R.unfold 的主要区别在于最后一个函数产生一个数组,这很容易用 R.last 解决:

const { curry, pipe, unfold, last } = R

const selfComposeWhile = curry(
  (pred, fn, init) => pipe(
    unfold(n => pred(n, fn(n)) && [n, fn(n)]),
    last
  )(init)
)

const { ifElse, gt, inc, dec, lt } = R

const incOrDec = ifElse(gt(30), inc, dec)

console.log(selfComposeWhile(lt, incOrDec, 0)) // -> 29
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>

【讨论】:

  • 哇。完美的。我知道这感觉就像我无法独自想要的东西,很高兴看到 Ramda 基本上内置了它。 :)
  • 啊,每次展开都会运行 f(n) 两次。不过,解决这个问题并不难。再次感谢:)
【解决方案2】:

我现在确定的解决方案。

我使用了 selfComposeWhile 并将其命名为 unfoldUntil。我不确定这是最好的名称,因为它不会返回列表。基本上是R.until,谓词可以访问上一个和下一个值。

为了使它们更加一致,我已将我的 while 行为更改为 until 行为(R.complement 谓词)。


展开直到

如果是输入的:

unfoldUntil: <T>(
  pred: (p:T, n:T) => boolean, 
  fn:   (a:T) => T, 
  init: T
) => T

已实现

const unfoldUntil = curry( 
  (pred, fn, init) => pipe(
    unfold(n =>
      isNil(n) ? 
      false : 
      call((next = fn(n)) => 
        (pred(n, next) ? 
        [next, null] : 
        [next, next])
      )
    ),
    last
  )(init)
);

注意:这永远不会将 null/undefined 传递给转换函数 (fn)。您可以使用返回 null 作为停止条件并返回前一个值的转换。否则,您将返回next 的第一个值,它会导致谓词返回 true。

【讨论】:

    猜你喜欢
    • 2017-02-14
    • 2017-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-10
    • 2018-05-27
    • 2020-02-29
    相关资源
    最近更新 更多