如何在Scala的列表中减去两个连续的元素?



我想在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
}

我有以下diffList =(1, 2, 1)的结果,但我想要diffList = (1,1)

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

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

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

就像我常说的,Scaladoc是你的朋友。
(另外,作为一个建议,学习函数式编程的最好方法是禁止自己使用可变性)

您可以使用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上运行的代码。

for(i <- 0 until (sortedList.size - 1)) yield sortedList(i + 1) - sortedList(i)

生成Vector(1,1),toList可以转换为list

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

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 } 

在列表上滑动(参见@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元素,以此类推。

最新更新