使用ramda.js

时间:2017-08-17 16:59:05

标签: javascript functional-programming ramda.js

我的代码中有一个模式,它一直反复出现,看起来应该很常见,但我不能为我的生活弄清楚它的名称是什么,或者是否有共同的方法来处理它:{{1使用一个带有参数的函数,该参数本身就是以map ed元素为参数的函数的结果。

这是模式本身。我已将我想要的函数命名为map(map-apply),但这似乎是错误的名称:

mapply

这究竟叫什么?我怎样才能在惯用的Ramda中实现它?看起来世界上有一个聪明人告诉我如何处理它的事情。

我的用例是做一些基本的准牛顿物理工作,对物体施加力。要计算一些力,你需要一些关于物体位置,质量,速度等的信息。一个(非常)简化的例子:

const mapply = (outer, inner) => el => outer(inner(el))(el)

有人可以告诉我:这是什么?你怎么会拉蒙达呢?我应该注意哪些陷阱,边缘情况,困难?有什么聪明的方法来处理它?<​​/ p>

(我搜索和搜索了 - SO,Ramda的GitHub回购,其他一些函数式编程资源。但也许我的Google-fu不是它需要的地方。如果我忽略了一些显而易见的东西,请道歉。谢谢!)

3 个答案:

答案 0 :(得分:0)

这是一个作文。它具体为compose(或pipe,如果您正在倒退)。

在数学中(比如说,考虑单变量微积分),你会有一些语句,如fxf(x),表示有一些函数f,它转换x,转换应该是别处描述......

当你看到(g º f)(x)时,你就会陷入疯狂。 &#34; G&F;#34; (或许多其他描述)。

(g º f)(x) == g(f(x))

看起来很熟悉?

const compose = (g, f) => x => g(f(x));

当然,您可以通过使用组合函数作为组合函数内部的操作来扩展此范例。

const tripleAddOneAndHalve = compose(halve, compose(add1, triple));
tripleAddOneAndHalve(3); // 5

对于变量版本,你可以做两件事之一,这取决于你是否想深入了解功能构成,或者只是稍微理顺一下。

// easier for most people to follow
const compose = (...fs) => x =>
  fs.reduceRight((x, f) => f(x), x);

// bakes many a noodle
const compose = (...fs) => x =>
  fs.reduceRight((f, g) => x => g(f(x)));

但是现在,如果你采取像咖喱或部分map这样的东西,例如:

const curry = (f, ...initialArgs) => (...additionalArgs) => {
  const arity = f.length;
  const args = [...initialArgs, ...additionalArgs];
  return args.length >= arity ? f(...args) : curry(f, ...args);
};

const map = curry((transform, functor) =>
  functor.map(transform));

const reduce = ((reducer, seed, reducible) =>
  reducible.reduce(reducer, seed));

const concat = (a, b) => a.concat(b);

const flatMap = curry((transform, arr) =>
  arr.map(transform).reduce(concat, []));

你可以做一些漂亮的事情:

const calculateCombinedAge = compose(
  reduce((total, age) => total + age, 0),
  map(employee => employee.age),
  flatMap(team => team.members));

const totalAge = calculateCombinedAge([{
  teamName: "A",
  members: [{ name: "Bob", age: 32 }, { name: "Sally", age: 20 }],
}, {
  teamName: "B",
  members: [{ name: "Doug", age: 35 }, { name: "Hannah", age: 41 }],
}]);  // 128

相当强大的东西。当然,所有这些都可以在Ramda中找到。

答案 1 :(得分:0)

const mapply0 = (outer, inner) => el => outer(inner(el))(el);

const mapply1 = (outer, inner) => R.converge(
  R.uncurryN(2, outer),
  [
    inner,
    R.identity,
  ],
);

const mapply2 = R.useWith(
  R.converge,
  [
    R.uncurry(2),
    R.prepend(R.__, [R.identity]),
  ],
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.24.1/ramda.min.js"></script>

我没有对此进行测试,但它可能会有效。

首先是你的功能。

第二个使用converge传递'el'通过内部函数然后传递身份函数并将它们传递给一个未经证实的外部版本。

R.uncurryN(2,outer)就像这个外部(内部(el),el),这意味着收敛可以提供参数。

第三个可能太过分但是它很有趣,你调用的是第一个参数作为外部的未发布版本,第二个作为包含内部和标识的数组,useWith这样做完全删除了函数定义溶液

我不确定这是否是您所寻找的,但这些是我找到的3种写作方式。

答案 2 :(得分:0)

从对问题的评论中解释:

mapply实际上是chain

R.chain(f, g)(x); //=> f(g(x), x)

嗯,大多数情况。在这种情况下,请注意x必须是数组。

我对问题的解决方案是:

const gravitated = R.map(
    R.chain(applyForce, R.compose(R.of, gravity))
)(bodies)

链接的Ramda文档在这种情况下并不十分有用:它只是简单地说,“链接映射列表中的函数并连接结果”。 (ramdajs.com/docs/#chain)

答案是潜伏在那里的第二个例子中,其中两个函数被传递给链并部分应用。直到看完这些答案之后我才看到它。

(感谢ftorbergiScott Sauyet。)