Ramda/Functional 编程基础

Basics with Ramda/Functional Programming

有效的部分

我已经阅读了很多文章并观看了一些关于 FP 的讨论 JavaScript 并决定试一试。

我知道这是基本的东西,但是再来一次?这里是:

const easy = [
  0,0,4,6,7,2,0,0,0,
  5,0,0,8,0,0,0,9,6,
  0,6,3,0,4,0,0,0,8,
  3,8,2,1,0,0,9,6,0,
  4,7,5,0,0,0,1,0,0,
  9,1,0,2,0,4,5,0,0,
  0,0,0,0,0,0,0,2,9,
  0,0,1,0,0,0,7,4,3,
  2,0,0,0,6,3,8,0,1
];

const expandSudoku = R.map(
  R.ifElse(
    R.equals(0),
    R.always(R.range(1,10));
    R.of
  )
);

console.log(expandSudoku(easy));

这需要一个数字数组并将其映射到一个数组数组中。

R.equals 让我很困惑

所以如果我错了请纠正我,但这实际上是同一件事(使用一些匿名包装函数):

const easy = [
  0,0,4,6,7,2,0,0,0,
  5,0,0,8,0,0,0,9,6,
  0,6,3,0,4,0,0,0,8,
  3,8,2,1,0,0,9,6,0,
  4,7,5,0,0,0,1,0,0,
  9,1,0,2,0,4,5,0,0,
  0,0,0,0,0,0,0,2,9,
  0,0,1,0,0,0,7,4,3,
  2,0,0,0,6,3,8,0,1
];

const expandSudoku = R.map(v =>
  R.ifElse(
    x => R.equals(0)(x),
    x => R.range(1,10),
    x => R.of(x)
  )(v)
);

console.log(expandSudoku(easy));

第一个问题:为什么我把R.equals(0)(x)改成R.equals(0, x)会报错?我认为 Ramda 的柯里化风格应该是一样的。这只是打字稿吗?编译后的 JavaScript 运行得很好。

The Error: Argument of type '(x: any) => (a: any) => boolean' is not assignable to parameter of type 'Pred'.


通过第二个参数传递?

我有这个辅助函数:

const pass2nd = R.curry((fn, arg1, arg2) => fn(arg2));

我是这样使用的:

const indexToCoord = R.applySpec({
  x: R.modulo(R.__,9), 
  y: R.compose(Math.floor, R.divide(R.__,9))
});
const makeCell = R.applySpec({
  coord: pass2nd(indexToCoord), // pass2nd to only get the second argument
  options: R.unary(R.ifElse(    // Unary to only get the first argument
    R.equals(0),
    R.always(R.range(1,10)),
    R.of
  ))
});
const mapIndexed = R.addIndex(R.map);
const expandSudoku = mapIndexed(makeCell);

console.log(expandSudoku(easy));

Ramda 有类似的东西吗?那是代码气味吗?怎么了?

最后:

什么时候构图太多了?是

const indexToCoord = R.applySpec({
  x: R.modulo(R.__,9), 
  y: R.compose(Math.floor, R.divide(R.__,9))
});

通常优于

const indexToCoord = v => ({
  x: v % 9,
  y: Math.floor(v/9)
});

?

First question: why do I get an error if I change R.equals(0)(x) to R.equals(0, x)? I thought with Ramda's style of currying, those should be the same. Is this just a typescript thing? The compiled JavaScript runs just fine.

应该是一样的,如果JS没问题,那肯定是TS题。恐怕我对 TS 的了解不够,无法告诉您如何修复它。

I have this helper function:

const pass2nd = R.curry((fn, arg1, arg2) => fn(arg2));

[ ... ] Does Ramda come with something similar? Is that a code-smell? What's up?

您可以使用 nthArg in a similar manner. But I tend to feel that if I have to reach for that, then I'm trying to hard to make points-free something that doesn't need to be. (Similarly with useWith 和其他一些 Ramda 函数。)

When is composition too much? Is

const indexToCoord = R.applySpec({
  x: R.modulo(R.__,9), 
  y: R.compose(Math.floor, R.divide(R.__,9))
});

generally preferable to

const indexToCoord = v => ({
  x: v % 9,
  y: Math.floor(v/9)
});

?

我会说恰恰相反。您的第二个功能非常可读。在我看来,采用无点样式的唯一重要原因是使代码更具可读性。这不是这样做的,我也不会打扰。


额外的警告。在这:

const expandSudoku = R.map(
  R.ifElse(
    R.equals(0),
    R.always(R.range(1,10)),
    R.of
  )
)

使用 always 可能会有问题。这取决于您如何使用结果。但是很多数独系统都使用了相当多的变异来简化性能所需的内容。如果您这样做,您应该知道 always 只是 returns 提供的缓存值。它不是克隆人。如果你以这种方式使用它并选择改变结果数组,你将为每个替换的 0 改变它。

如果这是一个问题,thunkify 可能是更好的选择:

const expandSudoku = R.map(
  R.ifElse(
    R.equals(0),
    R.thunkify (R.range) (1, 10),
    R.of
  )
)

基本上 thunkify (fn) (...args) 相当于 () => fn (...args)。在存在突变的情况下,这比 always 安全得多。

当然,如果你做的一切都是不变的,那么恭喜你!我很想看到结果。