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));
这需要一个数字数组并将其映射到一个数组数组中。
- 0 -> [1,2,3,4,5,6,7,8,9]
- n -> [n]
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
安全得多。
当然,如果你做的一切都是不变的,那么恭喜你!我很想看到结果。
有效的部分
我已经阅读了很多文章并观看了一些关于 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));
这需要一个数字数组并将其映射到一个数组数组中。
- 0 -> [1,2,3,4,5,6,7,8,9]
- n -> [n]
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)
toR.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
安全得多。
当然,如果你做的一切都是不变的,那么恭喜你!我很想看到结果。