Ramda selfComposeWhile

Ramda selfComposeWhile

问题:

我正在学习函数式编程

开个玩笑,还有……

我有一个辅助函数,它一遍又一遍地与自身组合一个函数,直到满足某些条件。就像 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 没有正确的尾递归,而且这个网站的同名 (Stack Overflow) 是我如何使用它的真正问题。

没有什么错误,但我一直在尝试通过将它们应用于虚拟问题来学习函数式编程技术,这是我为数不多的几个地方之一代码以绝对必要的方式脱颖而出。

我也有

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

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

问题:

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

R.unfold 主要做你做的,它接受一个种子值(init),并转换它,并且在每次迭代中它 returns 当前值和新种子价值。在每次迭代中,您需要决定继续或停止使用谓词。

你的函数和 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>

我目前确定的解决方案。

我将 selfComposeWhile 命名为 unfoldUntil。我不确定这是不是最好的名字,因为它 return 不是一个列表。它基本上是 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)。您可以使用 return 为空的转换作为停止条件,并 returned 先前的值。否则,您将 returned 使谓词 return 为真的 next 的第一个值。