2

我已经开始学习Ramda.js和函数式编程,并且对使用无点编程风格的函数式组合非常感兴趣,但是我很难理解其中的一些内容,我希望有人可以帮助说明:

假设我有一份人员名单 - 我只想获取 13-19 岁(青少年)之间的人。然后,我想将每个人映射到该人的getName()方法的返回值(如果它存在),或者他们的name属性。然后,我想.toUpperCase()点名。

如果我使用常规的 JS 原型方法,我不会使用Array.prototype.filter来获取青少年用户,然后Array.prototype.map. 出于性能原因,我会使用Array.prototype.reduce,其主体将由一个条件保护,该条件检查每个迭代项目是否符合青少年的标准。这样,我就少了一次迭代。Elijah Manor 在他的博客上有一篇关于此的文章

这是我使用R.filterand提出的无点 Ramda 代码R.map(按预期工作):

var people = [
  { name: 'Bob', gender: 'male', age: 22 },
  { name: 'Jones', gender: 'male', age: 15 },
  { name: 'Alice', gender: 'female', age: 19 },
  { name: 'Carol', gender: 'female', age: 32 },
  { name: 'Odu', gender: 'male', age: 25 },
  { name: 'Fred', gender: 'male', age: 55 },
  { name: 'Nicole', gender: 'female', age: 29 },
  { getName: function() { return 'David' }, gender: 'male', age: 23 }
]

var getUpcasedTeenagerNames = R.pipe(
  R.filter(
    R.propSatisfies(R.both(R.lte(13), R.gte(19)), 'age')
  ),
  R.map(
    R.pipe(
      R.ifElse(
        R.propIs(Function, 'getName'),
        R.invoker(0, 'getName'),
        R.prop('name')
      ),
      R.toUpper
    )
  )
)

getUpcasedTeenagerNames(people) // => ['JONES', 'ALICE']

我的问题是 - 我将如何重写上述算法的以下本机版本以使用无点 Ramda.js?

var getUpcasedTeenagerNames = function(people) {
  return people
    .reduce(function(teenagers, person) {
      var age = person.age
      if (age >= 13 && age <= 19) {
        var name
        if (typeof (name = person.getName) === 'function') {
          name = name()
        } else {
          name = person.name
        }
        teenagers.push(name.toUpperCase())
      }
      return teenagers
    }, [])
}

我试过用R.scan,看着使用R.reducedR.when但我担心我可能会错过一点点。

为了您的方便,我在 Ramda REPL 中包含了以下代码:http: //goo.gl/6hBi5k

4

1 回答 1

3

首先,我会以不同的方式分解问题。我会使用 Ramda 的R.__占位符来填充 and 的第一个参数R.lteR.gte以便它们更好地阅读。我喜欢用一个简单的下划线来给它起别名,所以它会显示为R.both(R.gte(_, 13), R.lte(_, 19)),我觉得它更具可读性。然后我会分离出在一个人身上找到名字的函数。这显然是一段独立的代码,将其取出会使主代码更具可读性。

最后,还有一件大事,如果您了解一些有关Transducers的知识,您将学到一个技巧,无需担心中间集合,这些中间集合是初始技术中可能出现的性能问题。

var _ = R.__;
var findName = R.ifElse(
  R.propIs(Function, 'getName'),
  R.invoker(0, 'getName'),
  R.prop('name')
); 

var getUpcasedTeenagerNames = R.into([], R.compose(
  R.filter(
    R.propSatisfies(R.both(R.gte(_, 13), R.lte(_, 19)), 'age')
  ),
  R.map(R.pipe(findName, R.toUpper))
));

getUpcasedTeenagerNames(people); //=> ["JONES", "ALICE"]

现在我根本不会担心性能问题,除非我发现了性能问题并表明这部分代码是罪魁祸首。但是如果我有,那么我可以通过使用转换器来修复它,并且由于两者都map已经filter工作,我需要做的就是切换我的合成方向(这里通过从管道更改为合成)并用into([]).

如果你有兴趣,这里有一篇关于 Ramda 中用户传感器的文章,以及另一个关于传感器的很好的介绍

如果我有一点时间,我会看看我是否可以将您的代码转换为免积分的解决方案,但请不要迷信免积分。在某些情况下,它是一种有用的技术,但我们不应该在不适合的地方使用它。

于 2016-01-25T20:42:22.767 回答