繁体   English   中英

通过ramda.js使用高阶函数进行映射

[英]Mapping using higher-order functions with ramda.js

我在我的代码,一再重复发生,似乎像它应该是相当常见的一种模式,但我不能为我的生活弄清楚它叫什么或是否有处理它常用方法: map使用函数ING是接受一个参数,该参数本身就是将map ed元素作为参数的函数的结果。

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

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

这到底叫什么? 如何在惯用的Ramda中实现它? 看起来好像世界上必须有聪明的人告诉我如何处理它。

我的用例是做一些基本的拟牛顿物理学工作,将力施加到对象上。 要计算一些力,您需要有关对象的一些信息-位置,质量,速度等。一个(非常)简化的示例:

const g = Vector.create(0, 1),
    gravity = ({ mass }) => Vector.multiply(mass)(g),
    applyForce = force => body => {
        const { mass } = body, 
            acceleration = Vector.divide(mass)(force)

        return R.merge(body, { acceleration })
    }

//...

const gravitated = R.map(mapply(applyForce, gravity))(bodies)

有人可以告诉我:这是什么? 你会如何Ramda-fy? 我应该注意哪些陷阱,边缘情况,困难? 有哪些明智的处理方式?

(我一直在搜索和搜索-SO,Ramda的GitHub存储库,其他一些功能编程资源。但是也许我的Google-fu并不需要它。如果我忽略了一些显而易见的内容,我深表歉意。谢谢!)

这是一个组成。 它是专门compose (如果您想倒退,可以使用pipe )。

在数学中(考虑一下单变量演算),您将有诸如fxf(x)类的语句表示有某个函数f转换x,并且该转换将在其他地方进行描述。

然后,当您看到(g º f)(x)时,就会变得疯狂。 “ G of F”(或许多其他描述)。

(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中获得。

 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,external)的工作方式与此外部(inner(el),el)相同,这意味着收敛可以提供参数。

第三个参数可能太远了,但还是很有趣,您正在调用converge,其中第一个参数是外部的未固化版本,第二个参数是包含内部和标识的数组,useWith这样做可以从解决方案中完全删除函数定义。

我不确定这是否是您要寻找的东西,但这是我发现的三种书写方式。

从对问题的评论中解释:

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)

答案隐藏在第二个示例中,其中两个函数传递给chain并部分应用。 在阅读完这些答案后,我才能看到。

(感谢ftorbergiScott Sauyet 。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM