简化
是的,写midElement 有一种更简单的方法。这感觉有点干净:
const div2 = divide (__, 2)
const lengthDiv2 = compose (floor, div2, length)
const midElement = chain (nth, lengthDiv2)
console.log (midElement ([8, 6, 7, 5, 3, 0, 9])) //=> 5
console.log (midElement ([8, 6, 7, 5, 3, 0])) //=> 5
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script><script>
const {divide, __, compose, length, chain, nth} = R
const {floor} = Math </script>
(我们在这里选择 nth 而不是 prop 只是因为它在语义上更正确。nth 特定于数组及其索引。prop 之所以起作用,只是因为 Javascript 在普通对象之上构建其数组的巧合。 )
chain 是一个有趣的函数。您可以在其FantasyLand specification 中找到更多详细信息。但对于我们的案例,重要的是它如何与函数一起使用。
chain (f, g) //=> (x) => f (g (x)) (x)
这就解释了(至少在这里)它是converge 的更简单替代方案。
请注意,此版本(与您的原始版本一样)在列表长度为偶数时选择两个中心值中的第二个。我通常发现我们更自然地选择第一个。也就是说,例如,midpoint([3, 6, 9, 12]) 通常是6。要改变这一点,我们可以简单地在除法之前添加一个减量操作:
const midpoint = chain(nth, compose(floor, divide(__, 2), dec, length))
但是为什么?
但是,Ramda 在这里并没有提供太多用处。 Ramda(免责声明:我是它的主要作者之一)为许多问题提供帮助。但它是一个工具,我不建议使用它,除非它能让你的代码更清晰。
而且这个版本在我看来更容易理解:
const midpoint = (xs) => xs[Math.floor ((xs.length / 2))]
console.log (midpoint ([8, 6, 7, 5, 3, 0, 9])) //=> 5
console.log (midpoint ([8, 6, 7, 5, 3, 0])) //=> 5
如果你想要上面的减量行为,或者这个版本:
const midpoint = (xs) => xs[Math.floor (((xs.length - 1) / 2))]
console.log (midpoint ([8, 6, 7, 5, 3, 0, 9])) //=> 5
console.log (midpoint ([8, 6, 7, 5, 3, 0])) //=> 7
另一种选择
但是有很多不同的方法可以编写这样的函数。虽然我不会真正推荐它,因为它的性能无法比较,递归解决方案非常优雅:
// choosing the first central option
const midpoint = (xs) => xs.length <= 2 ? xs[0] : midpoint (xs.slice(1, -1))
// choosing the second central option
const midpoint = (xs) => xs.length <= 2 ? xs[xs.length - 1] : midpoint (xs.slice(1, -1))
如果剩下的元素不超过两个,它们只取两个中心元素之一,否则在删除第一个和最后一个元素后递归地取剩余数组的中点。
要记住什么
我是 Ramda 的创始人,并为图书馆感到自豪。但我们需要记住,它只是一个库。它应该使某种编码风格更容易,但它不应该规定任何特定的风格。当它使您的代码更简单、更可维护、更一致或更高性能时使用它。永远不要仅仅因为你可以就使用它。