【问题标题】:How to subtract two consecutive element in a list in Scala?如何减去Scala列表中的两个连续元素?
【发布时间】:2021-05-04 10:08:13
【问题描述】:

我想用 Scala 中的数字减去列表中的两个连续元素。

例如:我有这个列表:

val sortedList = List(4,5,6)

我想要一个像diffList =(1, 1) 这样的输出列表,其中5-4 = 16-5 = 1

我尝试了以下代码:

var sortedList = List[Int]()
var diffList = List[Int]()

for (i <- 0 to (sortedList.length - 1) ;j <- i + 1 to sortedList.length - 1) 
{
    val diff = (sortedList(j) - sortedList(i))
    diffList = diffList :+ diff
}

我对@9​​87654326@ 有以下结果,但我想要diffList = (1,1)

这是因为 for 循环。它不会一次遍历两个变量(i 和 j)。

【问题讨论】:

    标签: list scala


    【解决方案1】:

    你不用可变性或命令式编程来解决这个问题,函数式编程帮你解决了。

    def consecutiveDifferences(data: List[Int]): List[Int] =
      if (data.isEmpty) List.empty
      else data.lazyZip(data.tail).map {
        case (x, y) => y - x
     }
    

    正如我常说的,Scaladoc 是你的朋友。
    (另外,作为建议,学习函数式编程的最佳方法是避免自己的可变性)

    【讨论】:

    • 谢谢。逻辑和java编程很不一样。
    • @Sandra 是的,学习 FP 需要一些时间,尤其是因为你需要从命令式编程中忘掉你的习惯。这就是为什么我的建议是强迫自己不要使用可变性并使用 Scaladoc 来发挥自己的优势。
    【解决方案2】:

    您可以使用sliding 方法,根据文档:

    /** Groups elements in fixed size blocks by passing a "sliding window"
     *  over them (as opposed to partitioning them, as is done in `grouped`.)
     *
     *  An empty collection returns an empty iterator, and a non-empty
     *  collection containing fewer elements than the window size returns
     *  an iterator that will produce the original collection as its only
     *  element.
     *  @see [[scala.collection.Iterator]], method `sliding`
     *
     *  @param size the number of elements per group
     *  @return An iterator producing ${coll}s of size `size`, except for a
     *          non-empty collection with less than `size` elements, which
     *          returns an iterator that produces the source collection itself
     *          as its only element.
     *  @example `List().sliding(2) = empty iterator`
     *  @example `List(1).sliding(2) = Iterator(List(1))`
     *  @example `List(1, 2).sliding(2) = Iterator(List(1, 2))`
     *  @example `List(1, 2, 3).sliding(2) = Iterator(List(1, 2), List(2, 3))`
     */
    

    那么,解决您的问题就很简单了:

    diffList = sortedList.sliding(2).collect {
      case Seq(a, b) =>
        b - a
    }.toList
    

    导致List(1,1)

    代码在Scastie 运行。

    【讨论】:

    • 非常好@Tomer Shetah
    • @MatthiasBerndt,collect的用法,就像filter + map。因此,通常在使用 collect 时,您不会检查模式匹配。如果diffList 的元素少于 2 个,则将不匹配,结果将如预期的那样为空列表。否则将匹配所有元素。
    • 此外,首先调用将产生长度为 2 的元素然后再次过滤掉的方法仍然是 hacky。最好不要使用不适当的sliding 方法,首先不要生成此类元素。只需执行sortedList.zip(sortedList.drop(1))。 Zvi Mints 的解决方案显然是更好的解决方案。
    • @MatthiasBerndt 你还是没抓住重点。过滤的唯一原因是短 (
    • @MatthiasBerndt BTW,如果你想增加滑动窗口,例如从第一个元素中减去后面的 2 个元素,使用zip 会更难,而使用sliding 会更容易。更不用说 10 个元素了。
    【解决方案3】:
    for(i <- 0 until (sortedList.size - 1)) yield sortedList(i + 1) - sortedList(i)
    

    yield Vector(1,1) 可以使用toList 转换为列表

    也可以通过以下函数实现:

      val sortedList = List(4,5,7)
      @tailrec
      def findDiffs(xs: List[Int])(seed: List[Int]): List[Int] = {
        if(xs.isEmpty || xs.size == 1) seed.reverse
        else {
          val currDiff = xs(1) - xs(0)
          findDiffs(xs.tail)(currDiff :: seed)
        }
      }
      val res = findDiffs(sortedList)(Nil)
      println(res)
    

    或者只是简单地使用 zip:

    sortedList.drop(1) zip sortedList map { case (x,y) => x - y } 
    

    【讨论】:

      【解决方案4】:

      在列表上滑动(参见@Tomer Shetah 的回答)会提供一个迭代器,这对于非常大的集合可能很方便,以避免/减少处理中的中间结构的数量。另一种方法包括压缩列表,使其自身移动一个(参见@Luis Miguel Mejía Suárez 和@Zvi Mints 的回答);在这方面,另一种移动然后压缩的方法是删除第一个元素,如

      xs.drop(1) zip xs map {case(a,b) => b-a}
      

      这可以通过删除任何数字n 来概括,这样我们减去第一个和n-th 元素,然后是第二个和n+1-th 元素,依此类推。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-11-09
        • 1970-01-01
        • 2019-01-08
        • 1970-01-01
        • 1970-01-01
        • 2019-01-22
        • 2020-12-05
        • 1970-01-01
        相关资源
        最近更新 更多