在参数顺序错误的地方自由点
point free where arguments are in the wrong order
我想使用 Ramda 的标准函数集编写一个函数,给定一个字典和一个键,它将增加键的值。例子
fn('foo', {}) // => {foo: 1}
fn('foo', {foo: 1}) // => {foo: 2}
我已经很接近了,但我缺少正确咖喱的方法。
我有一个方法需要一个键和一个对象,return还有一个:
// count :: Any -> Number
var count = R.compose(R.inc, R.defaultTo(0))
// countProp :: String -> Object -> Number
var countProp = R.curry(R.compose(count, (R.prop(R.__))))
countProp('foo', {foo:1}) // 2
countProp('foo', {}) // 1
现在我想return一个新的数据结构
// accum :: String -> Object -> Object
var accum = R.curry(function(key, obj){
return R.assoc(key, countProp(key, obj), obj)
})
accum('foo', {foo: 1}) // => {foo: 2}
但问题是,为了免除这一点,我必须弄清楚如何获取函数设置中的值,以便以正确的顺序进行柯里化。我究竟做错了什么?我应该以不同的方式设置此功能吗?我试图设置它,以便两个依赖函数都先获取密钥,然后再获取对象,但我遗漏了一些东西。我应该为此考虑一个特定的 Functor 吗?
谢谢!
您可以使用 R.lens
:
const fooLens = R.lens(R.prop('foo'), R.assoc('foo'));
fooLens.map(R.inc, {foo: 1, bar: 2}); // => {foo: 2, bar: 2}
fooLens.map(R.inc, {foo: 2, bar: 2}); // => {foo: 3, bar: 2}
fooLens.map(R.inc, {foo: 3, bar: 2}); // => {foo: 4, bar: 2}
Lenses 使得创建一系列值成为可能,而不会通过改变它来破坏后继值的完整性。
几点:
首先,如果@davidchambers 的解决方案满足您的需求,那就太好了。下个版本的 Ramda 发布并添加 lensProp 时会更好,这将使
var fooLens = R.lensProp('foo');
fooLens.map(R.inc, {foo: 1, bar: 2}); // => {foo: 2, bar: 2}
第二,你的原版功能和任一镜头版本有区别:
accum('foo', {bar: 1}); //=> {"bar":1,"foo":1}
fooLens.map(R.inc, {bar: 1}); //=> {"bar":1,"foo":null}
第三,尽管如此,如果您有兴趣确定如何以无积分的方式包装您的函数,Ramda 有几个函数可以提供帮助。有一个辅助函数 nthArg
which does nothing but return a function that return the nth argument of the outer function in which it's called. Then there are several functions that act as extended versions of compose
including useWith
and converge
.
您可以像这样使用它们:
var accum = R.converge(R.assoc, R.nthArg(0), countProp, R.nthArg(1));
accum('foo', {foo: 1, bar: 2}); // => {foo: 2, bar: 2}
accum('foo', {bar: 2}); // => {foo: 1, bar: 2}
在此代码中,converge
将参数(key
和 obj
)传递给作为参数传递的每个函数(第一个除外),然后传递结果每一个都是第一个函数。
Finally,尽管这展示了一种无积分编写此代码的方法,而且它最终并不太可怕,但可以说它不如你的早期版本那么清晰t点免。我喜欢无积分代码。但有时我们会迷恋它,无缘无故地使代码点免费。如果您最终无法使用 lens
版本,您可能需要仔细考虑免积分解决方案是否真的比替代方案更清晰。
我想使用 Ramda 的标准函数集编写一个函数,给定一个字典和一个键,它将增加键的值。例子
fn('foo', {}) // => {foo: 1}
fn('foo', {foo: 1}) // => {foo: 2}
我已经很接近了,但我缺少正确咖喱的方法。
我有一个方法需要一个键和一个对象,return还有一个:
// count :: Any -> Number
var count = R.compose(R.inc, R.defaultTo(0))
// countProp :: String -> Object -> Number
var countProp = R.curry(R.compose(count, (R.prop(R.__))))
countProp('foo', {foo:1}) // 2
countProp('foo', {}) // 1
现在我想return一个新的数据结构
// accum :: String -> Object -> Object
var accum = R.curry(function(key, obj){
return R.assoc(key, countProp(key, obj), obj)
})
accum('foo', {foo: 1}) // => {foo: 2}
但问题是,为了免除这一点,我必须弄清楚如何获取函数设置中的值,以便以正确的顺序进行柯里化。我究竟做错了什么?我应该以不同的方式设置此功能吗?我试图设置它,以便两个依赖函数都先获取密钥,然后再获取对象,但我遗漏了一些东西。我应该为此考虑一个特定的 Functor 吗?
谢谢!
您可以使用 R.lens
:
const fooLens = R.lens(R.prop('foo'), R.assoc('foo'));
fooLens.map(R.inc, {foo: 1, bar: 2}); // => {foo: 2, bar: 2}
fooLens.map(R.inc, {foo: 2, bar: 2}); // => {foo: 3, bar: 2}
fooLens.map(R.inc, {foo: 3, bar: 2}); // => {foo: 4, bar: 2}
Lenses 使得创建一系列值成为可能,而不会通过改变它来破坏后继值的完整性。
几点:
首先,如果@davidchambers 的解决方案满足您的需求,那就太好了。下个版本的 Ramda 发布并添加 lensProp 时会更好,这将使
var fooLens = R.lensProp('foo');
fooLens.map(R.inc, {foo: 1, bar: 2}); // => {foo: 2, bar: 2}
第二,你的原版功能和任一镜头版本有区别:
accum('foo', {bar: 1}); //=> {"bar":1,"foo":1}
fooLens.map(R.inc, {bar: 1}); //=> {"bar":1,"foo":null}
第三,尽管如此,如果您有兴趣确定如何以无积分的方式包装您的函数,Ramda 有几个函数可以提供帮助。有一个辅助函数 nthArg
which does nothing but return a function that return the nth argument of the outer function in which it's called. Then there are several functions that act as extended versions of compose
including useWith
and converge
.
您可以像这样使用它们:
var accum = R.converge(R.assoc, R.nthArg(0), countProp, R.nthArg(1));
accum('foo', {foo: 1, bar: 2}); // => {foo: 2, bar: 2}
accum('foo', {bar: 2}); // => {foo: 1, bar: 2}
在此代码中,converge
将参数(key
和 obj
)传递给作为参数传递的每个函数(第一个除外),然后传递结果每一个都是第一个函数。
Finally,尽管这展示了一种无积分编写此代码的方法,而且它最终并不太可怕,但可以说它不如你的早期版本那么清晰t点免。我喜欢无积分代码。但有时我们会迷恋它,无缘无故地使代码点免费。如果您最终无法使用 lens
版本,您可能需要仔细考虑免积分解决方案是否真的比替代方案更清晰。