【问题标题】:Is there alternative to get middle element from array using Ramda?是否可以使用 Ramda 从数组中获取中间元素?
【发布时间】:2020-06-02 18:28:28
【问题描述】:

我尝试了这段代码,它产生了我想要的结果:

const {
  __,
  compose,
  converge,
  divide,
  identity,
  length,
  prop
} = require("ramda");

const div2 = divide(__, 2);
const lengthDiv2 = compose(Math.floor, div2, length);
const midElement = converge(prop, [lengthDiv2, identity]);

console.log(midElement([1, 5, 4]); //5

但我不知道还有其他方法可以从数组中获取属性,尤其是 midElement 函数的其他一些实现吗?

【问题讨论】:

    标签: javascript functional-programming ramda.js


    【解决方案1】:

    您可以通过链接R.nthlengthDiv2 创建midElement,因为根据R.chain 文档(和@ScottSauyet):

    如果第二个参数是一个函数,chain(f, g)(x) 等价于 f(g(x), x)。

    在这种情况下,glengthDiv2f 是 R.nth,x 是数组。因此,结果将是R.nth(lengthDiv2(array), array),它将返回中间项。

    const { compose, flip, divide, length, chain, nth } = R;
    
    const div2 = flip(divide)(2); // create the function using flip
    const lengthDiv2 = compose(Math.floor, div2, length);
    const midElement = chain(nth, lengthDiv2); // chain R.nth and lengthDiv2
    
    console.log(midElement([1, 5, 4])); //5
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>

    【讨论】:

    • 在我打字的时候进来了。请注意,函数上的 chain 行为虽然由 Ramda 实现,但与 FantasyLand specs 是唯一的逻辑匹配。
    【解决方案2】:

    简化

    是的,写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 的创始人,并为图书馆感到自豪。但我们需要记住,它只是一个库。它应该使某种编码风格更容易,但它不应该规定任何特定的风格。当它使您的代码更简单、更可维护、更一致或更高性能时使用它。永远不要仅仅因为你可以就使用它。

    【讨论】:

      猜你喜欢
      • 2017-01-27
      • 2011-09-01
      • 1970-01-01
      • 2012-07-25
      • 2019-03-02
      • 1970-01-01
      • 2012-03-07
      • 2011-10-11
      • 2022-11-13
      相关资源
      最近更新 更多