将 R.filter 和 R.map 重构为 pointfree 风格的 R.reduce
Refactor R.filter and R.map to just R.reduce in pointfree style
我已经开始学习 Ramda.js 和函数式编程,并且对使用 pointfree 编程风格的函数式组合非常感兴趣,但是我很难理解其中的一些,我希望有人可以帮忙说明一下:
假设我有一个人员列表 - 我只想获取年龄在 13-19 岁(青少年)之间的人员。然后,我想将每个人映射到该人的 getName()
方法(如果存在)的 return 值,否则映射到他们的 name
属性。然后,我想在名字上调用.toUpperCase()
。
如果我使用常规的 JS 原型方法,我不会使用 Array.prototype.filter
来获取青少年用户然后 Array.prototype.map
。出于性能原因,我会使用 Array.prototype.reduce
,它的主体将由一个条件来保护,该条件检查每个迭代项目是否满足青少年的标准。这样,我就少了一次迭代。 Elijah Manor has an article about this on his blog.
这是我使用 R.filter
和 R.map
想出的 pointfree 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']
我的问题是 - 我将如何重写上述算法的以下原生版本以使用 pointfree 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.reduced
和 R.when
但恐怕我可能漏掉了一点点。
为了您的方便,我在 Ramda REPL 中包含了这段代码:http://goo.gl/6hBi5k
首先,我会以不同的方式分解问题。我会使用 Ramda 的 R.__
占位符来填充 R.lte
和 R.gte
的第一个参数,以便它们更好地阅读。我喜欢用一个简单的下划线作为别名,所以这将显示为 R.both(R.gte(_, 13), R.lte(_, 19))
,我发现它更具可读性。然后我会分离出在一个人身上找到名字的功能。这显然是 stand-alone 的一小段代码,将其取出可以使主要代码更具可读性。
最后,重要的是,如果您稍微了解 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([])
.
包裹起来
如果您有兴趣,这里 an article on user transducers in Ramda, and another good intro 给传感器。
如果我有一点时间,我会看看是否可以将您的代码转换为 points-free 解决方案,但请不要迷信 points-free。这是一个有用的
技术在某些情况下,但我们不应该被迫在它不容易适应的地方使用它。
我已经开始学习 Ramda.js 和函数式编程,并且对使用 pointfree 编程风格的函数式组合非常感兴趣,但是我很难理解其中的一些,我希望有人可以帮忙说明一下:
假设我有一个人员列表 - 我只想获取年龄在 13-19 岁(青少年)之间的人员。然后,我想将每个人映射到该人的 getName()
方法(如果存在)的 return 值,否则映射到他们的 name
属性。然后,我想在名字上调用.toUpperCase()
。
如果我使用常规的 JS 原型方法,我不会使用 Array.prototype.filter
来获取青少年用户然后 Array.prototype.map
。出于性能原因,我会使用 Array.prototype.reduce
,它的主体将由一个条件来保护,该条件检查每个迭代项目是否满足青少年的标准。这样,我就少了一次迭代。 Elijah Manor has an article about this on his blog.
这是我使用 R.filter
和 R.map
想出的 pointfree 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']
我的问题是 - 我将如何重写上述算法的以下原生版本以使用 pointfree 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.reduced
和 R.when
但恐怕我可能漏掉了一点点。
为了您的方便,我在 Ramda REPL 中包含了这段代码:http://goo.gl/6hBi5k
首先,我会以不同的方式分解问题。我会使用 Ramda 的 R.__
占位符来填充 R.lte
和 R.gte
的第一个参数,以便它们更好地阅读。我喜欢用一个简单的下划线作为别名,所以这将显示为 R.both(R.gte(_, 13), R.lte(_, 19))
,我发现它更具可读性。然后我会分离出在一个人身上找到名字的功能。这显然是 stand-alone 的一小段代码,将其取出可以使主要代码更具可读性。
最后,重要的是,如果您稍微了解 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([])
.
如果您有兴趣,这里 an article on user transducers in Ramda, and another good intro 给传感器。
如果我有一点时间,我会看看是否可以将您的代码转换为 points-free 解决方案,但请不要迷信 points-free。这是一个有用的 技术在某些情况下,但我们不应该被迫在它不容易适应的地方使用它。