3

我想使用 Ramda 的标准函数集编写一个函数,该函数集给定一个字典和一个键,它将增加键的值。例子

fn('foo', {}) // => {foo: 1}
fn('foo', {foo: 1}) // => {foo: 2}

我已经很接近了,但我错过了一种正确咖喱的方法。

我有一个方法,它接受一个键和一个对象并返回一个:

// 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

现在我想返回一个新的数据结构

// 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}

但问题是,为了使这一点免费,我必须弄清楚如何在函数设置中获取值,以便以正确的顺序进行咖喱。我究竟做错了什么?我应该以不同的方式设置此功能吗?我试图设置它,以便两个依赖函数都首先获取密钥,然后是对象,但我错过了一些东西。我应该为此考虑一个特定的仿函数吗?

谢谢!

4

2 回答 2

5

几点:

首先,如果@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只返回一个函数,该函数返回调用它的外部函数的第 n 个参数。然后有几个函数充当 include 和 的扩展compose版本。useWithconverge

你可以像这样使用它们:

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将参数 (keyobj) 传递给作为参数传递的每个函数,第一个函数除外,然后将每个函数的结果传递给第一个函数。


最后,虽然这显示了一种编写无点代码的方法,而且它最终并没有太可怕,但可以说它比你的早期版本不那么清晰。我喜欢免积分代码。但有时我们会迷恋它,无缘无故地让代码点免费。如果您最终无法使用某个lens版本,您可能需要仔细考虑无积分解决方案是否实际上比替代方案更清晰。

于 2015-04-10T00:27:41.307 回答
4

你可以使用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}

镜头可以创建一系列值,而不会通过变异来破坏继承值的完整性。

于 2015-04-09T21:33:51.060 回答