【问题标题】:Refactor R.filter and R.map to just R.reduce in pointfree style将 R.filter 和 R.map 重构为无点风格的 R.reduce
【发布时间】:2016-05-01 17:49:09
【问题描述】:

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

假设我有一个人员列表 - 我只想获取 13-19 岁(青少年)之间的人。然后,我想将每个人映射到该人的 getName() 方法(如果存在)的返回值,否则映射到他们的 name 属性。然后,我想打电话给.toUpperCase()

如果我使用常规的 JS 原型方法,我不会使用 Array.prototype.filter 来获取青少年用户,然后使用 Array.prototype.map。出于性能原因,我将使用Array.prototype.reduce,其主体将由一个条件保护,该条件检查每个迭代项目是否符合青少年的标准。这样,我就少了一次迭代。 Elijah Manor has an article about this on his blog.

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

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

【问题讨论】:

标签: javascript functional-programming reduce pointfree ramda.js


【解决方案1】:

首先,我会以不同的方式分解问题。我会使用 Ramda 的R.__ 占位符来填充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"]

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

如果您有兴趣,这里是 an article 用于 Ramda 中的用户传感器,another good intro 用于传感器。

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

【讨论】:

  • 哇,传感器是一个很棒的概念——显然我还有很多东西要学。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多