Scala: Indexed Seq instead of List in for loop - scala

The following code shows a type mismatch error :
def f(arr:List[Int]): List[Int] =
for(num <- 0 to arr.length-1; if num % 2 == 1) yield arr(num)
It is says that it found an IndexedSeq instead of a List. The following works :
def f(arr:List[Int]): List[Int] =
for(num <- (0 to arr.length-1).toList; if num % 2 == 1) yield arr(num)
I have used i <- a to b in a for loop before but haven't seen this error before. Can someone please explain why the format i <- a to b cannot be used here ?

because 0 to arr.length-1 return type is: IndexedSeq[Int], so when execute for yield it also will yield result with IndexedSeq[Int] type.
The correct function define:
def f(arr:List[Int]):IndexedSeq[Int] = for( num <- 0 to arr.length-1 if num%2==1) yield arr(num)
And
for( num <- 0 to arr.length-1 if num%2==1) yield arr(num)
will translate to:
scala> def f(arr:List[Int]) = (0 to arr.length-1).filter(i => i%2==1).map(i => arr(i))
f: (arr: List[Int])scala.collection.immutable.IndexedSeq[Int]
So we can see the return type is decided by 0 to arr.length-1 type.
and (0 to arr.length-1).toList is changing the return IndexedSeq[int] type to List[Int] type, so for yield will generate result with type of List[Int].

In Scala, for each iteration of your for loop, yield generates a value which will be remembered. The type of the collection that is returned is the same type that you were iterating over, so a List yields a List, a IndexedSeq yields a IndexedSeq, and so on.
The type of (0 to arr.length-1) is scala.collection.immutable.Range, it's Inherited from scala.collection.immutable.IndexedSeq[Int]. So, in the first case, the result is IndexedSeq[Int], but the return type of function f is List[Int], obviously it doesn't work. In the second case, a List yields a List, and the return type of f is List[Int].
You can also write function f as follow:
def f(arr: List[Int]): IndexedSeq[Int] = for( a <- 1 to arr.length-1; if a % 2 == 1) yield arr(a)
Another example:
scala> for (i <- 1 to 5) yield i
res0: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5)
scala> for (e <- Array(1, 2, 3, 4, 5)) yield e
res1: Array[Int] = Array(1, 2, 3, 4, 5)

In scala for is a syntax sugar, where:
for (i <- a to b) yield func(i)
translate to:
RichInt(a).to(b).map({ i => func(i) })
RichInt.to returns a Range
Range.map returns a IndexedSeq

Related

Swap all Integers of a List with Scala

If I have a swap method, which can swap two Integers in a List:
def swap[E]: (List[E], Int, Int) => List[E] = (ls, i, j) =>
ls.updated(i, ls(j)).updated(j, ls(i))
And now I want to swap all Integers of a list using this method. That means the result should be like this:
swapAll(List(1,2,3)) == List(List(2,1,3), List(3,2,1), List(1,3,2))
I thought of something like this:
def swapAll: List[Int] => List[List[Int]] = ls => for(i <- 0 to ls.length; j <- i to ls.length) yield List(swap(ps, i, j))
But it doesn't work, does somebody have an Idea?
Almost there.
def swap[E](ls: List[E], i: Int, j: Int): List[E] = {
ls.updated(i, ls(j)).updated(j, ls(i))
}
def swapAll(ps: List[Int]): List[List[Int]] = {
val n = ps.size
(for {
i <- 0 until n
j <- (i + 1) until n
} yield swap(ps, i, j))(collection.breakOut)
}
Example:
swapAll(List(1, 2, 3))
// List(List(2, 1, 3), List(3, 2, 1), List(1, 3, 2))
The breakOut is a special explicitly inserted CanBuildFrom. It's necessary because without it, the type of the produced collection is some weird IndexedSequence derived from the 0 until n Range, but you want a List instead.

Type mismatch in Scala's for-comprehension

I have tried to define a recursive Scala function that looks something like this:
def doSomething: (List[List[(Int, Int)]], List[(Int, Int)], Int, Int) => List[Int] =
(als, rs, d, n) =>
if (n == 0) {
for (entry <- rs if (entry._1 == d)) yield entry._2
} else {
for (entry <- rs; adj <- als(entry._1)) yield doSomething(als, rs.::((adj._1, adj._2 + entry._2)), d, n - 1)
}
Now, the compiler tells me:
| | | | | | <console>:17: error: type mismatch;
found : List[List[Int]]
required: List[Int]
for (entry <- rs; adj <- als(entry._1)) yield doSomething(als, rs.::((adj._1, adj._2 + entry._2)), d, n - 1)
^
I cannot figure out what the problem is. I'm sure that I'm using <- correctly. On the other hand, I'm a Scala newbie coming from the Java world...
Regarding the types of the input:
als : List[List[(Int,Int)]],
rs : List[(Int,Int)],
d and n : Int
The compiler error appears as soon as I tell IntelliJ to send my code to the Scala console.
When you yield an A when iterating on a List, you return a List[A]. doSomething returns a List[Int], so by yielding that you return a List[List[Int]]. You can unroll that like this:
def doSomethingElse(als: List[List[(Int, Int)]], rs: List[(Int, Int)], d: Int, n: Int): List[Int] =
if (n == 0) {
for ((k, v) <- rs if k == d) yield v
} else {
for {
(k, v) <- rs
(adjk, adjv) <- als(k)
item <- doSomethingElse(als, (adjk, adjv + v) :: rs, d, n - 1)
} yield item
}
Notice that I also used a method notation for brevity and destructured the pairs and leveraged the right-associativity of methods whose name ends in : for readability, feel free to use whatever convention you might want (but I don't see really a reading why having a method that returns a constant function (maybe you'd want to just use a val to declare it).
As a further note, you are using random access on a linear sequence (als(k)), you may want to consider an indexed sequence (like a Vector). More info on the complexity characteristics of the Scala Collection API can be found here.
for test purpose I created some sample data that meets the input datatypes as
val als = List(List((1,2), (3,4)), List((1,2), (3,4)), List((1,2), (3,4)))
//als: List[List[(Int, Int)]] = List(List((1,2), (3,4)), List((1,2), (3,4)), List((1,2), (3,4)))
val rs = List((1,2), (2,3))
//rs: List[(Int, Int)] = List((1,2), (2,3))
val d = 1
//d: Int = 1
val n = 3
//n: Int = 3
And in you doSomething function when n == 0 you are doing
for (entry <- rs if (entry._1 == d)) yield entry._2
//res0: List[Int] = List(2)
You can see that the return type is List[Int]
And for the else part you are calling recursively doSomething.
I have created dummy doSomething method of yours as your doSomething function definition lacks input variables as
def dosomething(nn: Int)={
for (entry <- rs if (entry._1 == d)) yield entry._2
}
and I call the method recursively as
for (entry <- rs; adj <- als(entry._1)) yield dosomething(0)
//res1: List[List[Int]] = List(List(2), List(2), List(2), List(2))
Clearly you can see that the second nested for loop is returning List[List[Int]]
And thats what the compiler is warning you
error: type mismatch;
found : List[List[Int]]
required: List[Int]
I hope the answer is helpful

How to avoid calling toVector on each Scala for comprehension / yield?

I have several methods that operate on Vector sequences and the following idiom is common when combining data from multiple vectors into a single one with the use of a for comprehension / yield:
(for (i <- 0 until y.length) yield y(i) + 0.5*dy1(i)) toVector
Notice the closing toVector and the enclosing parentheses around the for comprehension. I want to get rid of it because it's ugly, but removing it produces the following error:
type mismatch;
found : scala.collection.immutable.IndexedSeq[Double]
required: Vector[Double]
Is there a better way of achieving what I want that avoids explicitly calling toVector many times to essentially achieve a non-operation (converting and indexed sequence...to an indexed sequence)?
One way to avoid collection casting, e.g. toVector, is to invoke, if possible, only those methods that return the same collection type.
y.zipWithIndex.map{case (yv,idx) => yv + 0.5*dy1(idx)}
for yield on Range which you are using in your example yields a Vector[T] by default.
example,
scala> val squares= for (x <- Range(1, 3)) yield x * x
squares: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 4)
check the type,
scala> squares.isInstanceOf[Vector[Int]]
res14: Boolean = true
Note that Vector[T] also extends IndexedSeq[T].
#SerialVersionUID(-1334388273712300479L)
final class Vector[+A] private[immutable] (private[collection] val startIndex: Int, private[collection] val endIndex: Int, focus: Int)
extends AbstractSeq[A]
with IndexedSeq[A]
with GenericTraversableTemplate[A, Vector]
with IndexedSeqLike[A, Vector[A]]
with VectorPointer[A #uncheckedVariance]
with Serializable
with CustomParallelizable[A, ParVector[A]]
That's why above result is also an instance of IndexedSeq[T],
scala> squares.isInstanceOf[IndexedSeq[Int]]
res15: Boolean = true
You can define the type of your result as IndexedSeq[T] and still achieve what you want with Vector without explicitly calling .toVector
scala> val squares: IndexedSeq[Int] = for (x <- Range(1, 3)) yield x * x
squares: IndexedSeq[Int] = Vector(1, 4)
scala> squares == Vector(1, 4)
res16: Boolean = true
But for yield on Seq[T] gives List[T] by default.
scala> val squares = for (x <- Seq(1, 3)) yield x * x
squares: Seq[Int] = List(1, 9)
Only in that case if you want vector you must .toVector the result.
scala> squares.isInstanceOf[Vector[Int]]
res21: Boolean = false
scala> val squares = (for (x <- Seq(1, 3)) yield x * x).toVector
squares: Vector[Int] = Vector(1, 9)

Can you define a value (in a if) in a for comprehension in Scala for use in yield

Is it possible to define a value (in a if) in a for comprehension in Scala for use in yield.
I want to do this to avoid a potential expensive evaluation two times.
An example to illustrate.
for {
bar <- bars if expensive(bar) > 5
} yield (bar, expensive(bar))
How about this:
for {
bar <- bars
exp = expensive(bar)
if exp > 5
} yield (bar, exp)
Yes, you can:
scala> List(1,2,3,4,5)
res0: List[Int] = List(1, 2, 3, 4, 5)
scala> for(n <- res0; val b = n % 2; if b==1) yield b
res2: List[Int] = List(1, 1, 1)

Collection type generated by for with yield

When I evaluate a for in Scala, I get an immutable IndexedSeq (a collection with array-like performance characteristics, such as efficient random access):
scala> val s = for (i <- 0 to 9) yield math.random + i
s: scala.collection.immutable.IndexedSeq[Double] = Vector(0.6127056766832756, 1.7137598183155291, ...
Does a for with a yield always return an IndexedSeq, or can it also return some other type of collection class (a LinearSeq, for example)? If it can also return something else, then what determines the return type, and how can I influence it?
I'm using Scala 2.8.0.RC3.
Thanks michael.kebe for your comment.
This explains how for is translated to operations with map, flatMap, filter and foreach. So my example:
val s = for (i <- 0 to 9) yield math.random + i
is translated to something like this (I'm not sure if it's translated to map or flatMap in this case):
val s = (0 to 9) map { math.random + _ }
The result type of operations like map on collections depends on the collection you call it on. The type of 0 to 9 is a Range.Inclusive:
scala> val d = 0 to 9
d: scala.collection.immutable.Range.Inclusive with scala.collection.immutable.Range.ByOne = Range(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
The result of the map operation on that is an IndexedSeq (because of the builder stuff inside the collections library).
So, to answer my question: the result of a for (...) yield ... depends on what type is inside the parantheses. If I want a List as the result, I could do this:
scala> val s = for (i <- List.range(0, 9)) yield math.random + i
s: List[Double] = List(0.05778968639862214, 1.6758775042995566, ...
You can always transform a range to a list using toList:
> val s = for (i <- (0 to 9).toList) yield math.random + i
> s : List[Double]