问题
I would like to subtract two consecutive element in a list with numbers in Scala.
For example : I have this list :
val sortedList = List(4,5,6)
I would like to have an output list like diffList =(1, 1)
where 5-4 = 1
and 6-5 = 1
.
I tried the following code:
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
}
I have the following result for diffList =(1, 2, 1)
but I want diffList = (1,1)
.
It's because of the for loop. it does not iterate over the two variables (i and j) at once.
回答1:
You do not mutability nor imperative programming to solve this problem, functional programming got you covered.
def consecutiveDifferences(data: List[Int]): List[Int] =
if (data.isEmpty) List.empty
else data.lazyZip(data.tail).map {
case (x, y) => y - x
}
As I always say, the Scaladoc is your friend.
(Also, as an advice, the best way to learn functional programming is to forbid yourself from mutability)
回答2:
You can use the sliding method, which according to the docs:
/** 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))` */
Then, solving your query is pretty straight forward:
diffList = sortedList.sliding(2).collect {
case Seq(a, b) =>
b - a
}.toList
Which results in List(1,1)
Code run at Scastie.
回答3:
for(i <- 0 until (sortedList.size - 1)) yield sortedList(i + 1) - sortedList(i)
yield Vector(1,1)
which can be converted to list with toList
That's can be also achieved with the following function:
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)
Or just easily with zip:
sortedList.tail zip sortedList map { case (x,y) => x - y }
Note: will throw UnsupportedOperationException
exception on empty sortedList, thus:
sortedList match {
case Nil => List.empty
case list @ _ :: tail => list zip tail
}
and then .map { case (x, y) => x - y }
Or use .drop(1)
instead of tail
回答4:
Sliding (see answer by @Tomer Shetah) over a list delivers an iterator, which may prove convenient for very large collections to avoid/reduce the amount of intermediate structures in the processing. Another approach includes the zipping of the list with itself shifted by one (see answers by @Luis Miguel Mejía Suárez and @Zvi Mints); in this regard another approach to shifting and then zipping is by dropping the first element as in
xs.drop(1) zip xs map {case(a,b) => b-a}
This can be generalised by dropping any number n
so that we subtract the first and the n
-th elements, then the second and the n+1
-th elements, and so forth.
来源:https://stackoverflow.com/questions/65972252/how-to-subtract-two-consecutive-element-in-a-list-in-scala