Type mismatch in Scala's for-comprehension - scala

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

Related

A "Simple' Scala question but took me long time to debug

Please check the two pieces of script as above.
genComb4(lst) works since I put z <- genComb4(xs) before i <- 0 to x._2 in the for-comprehension; genComb(lst) does not work since I change the order of these two lines in for-comprehension.
It took me almost half day to find this bug, but I cannot explain it by myself. Could you tell me why this happened?
Thank you very much in advance.
// generate combinations
val nums = Vector(1, 2, 3)
val strs = Vector('a', 'b', 'c')
val lst: List[(Char, Int)] = strs.zip(nums).toList
def genComb4(lst: List[(Char, Int)]): List[List[(Char, Int)]] = lst match {
case Nil => List(List())
case x :: xs =>
for {
z <- genComb4(xs) // correct
i <- 0 to x._2 // correct
} yield ( (x._1, i) :: z)
}
genComb4(lst)
def genComb(lst: List[(Char, Int)]): List[List[(Char, Int)]] = lst match {
case Nil => List(List())
case x :: xs =>
for {
i <- (0 to x._2) // wrong
z <- genComb(xs) // wrong
} yield ( (x._1, i) :: z)
}
genComb(lst)
It's because of different types of container in for comprehension. When you start for-comprehension from line: i <- (0 to x._2) it's set type of result container as IndexedSeq but in case where first line is z <- genComb4(xs) the type of result container is List, take a look:
val x = 'a' -> 2
val indices: Seq[Int] = 0 to x._2
val combs: List[List[(Char, Int)]] = genComb4(List(x))
// indexed sequence
val indicesInFor: IndexedSeq[(Char, Int)] = for {
i <- 0 to x._2
} yield (x._1, i)
// list
val combsInFor: List[List[(Char, Int)]] = for {
z <- genComb4(List(x))
} yield z
so for make your second case is working, you should cast (0 to x._2).toList:
val indicesListInFor: List[(Char, Int)] = for {
i <- (0 to x._2).toList
} yield (x._1, i)
result code should be:
def genComb(lst: List[(Char, Int)]): List[List[(Char, Int)]] = lst match {
case Nil => List(List())
case x :: xs =>
for {
i <- (0 to x._2).toList
z <- genComb(xs)
} yield ( (x._1, i) :: z)
}
genComb(lst)
You should remember about type of starting line in for-comprehension and inheritance of scala collections. If next types in for-comprehension can't be converted by inheritance rules to the first expression line type you should take care about it by yourself.
Good practise is unwrap for-expression into flatMap, map and withFilter functions, then you will find miss-typing or something else faster.
useful links:
how does yield work, scala documentation
quick review of scala for-comprehensions
for-expressions, scala documentation

Scala: Indexed Seq instead of List in for loop

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

Generic function for multiple generators in for comprehensions in Scala

Let's say I want to create all possible combinations of letters "a" and "b". For combinations of length 2 using for-comprehensions it will be:
for {
x <- Seq("a", "b")
y <- Seq("a", "b")
} yield x + y
And for combinations of length 3 it will be:
for {
x <- Seq("a", "b")
y <- Seq("a", "b")
z <- Seq("a", "b")
} yield x + y + z
Pretty similar. Is this possible to abstract this pattern and write generic function?
I can think of such signature:
def genericCombine[A,B](length: Int, elements: Seq[A])(reducer: Seq[A] => B): Seq[B]
How can I parametrize number of generators used in for comprehension?
This is more like permutations with replacement than combinations, and a recursive implementation is fairly straightforward:
def select(n: Int)(input: List[String]): List[String] =
if (n == 1) input else for {
c <- input
s <- select(n - 1)(input)
} yield c + s
Which works as expected:
scala> select(2)(List("a", "b"))
res0: List[String] = List(aa, ab, ba, bb)
scala> select(3)(List("a", "b"))
res1: List[String] = List(aaa, aab, aba, abb, baa, bab, bba, bbb)
(You should of course check for invalid input in a real application.)

Scala find an Int between a List of Int

I have an Int and I need to find in a List of Ints the upper and lower bounds for this Int.
For example:
In a List(1,3,6,9), when I ask for 2, I should get 1 and 3. Is there any pre-built function in the Scala collection API that I can use? I know that I can achieve this using the filter function, but I'm looking for an already existing API if any?
So, not built in, but here you go. Since you want return nothing for (e.g.) 0 and 10, we need to return an option.
var rs = List(1, 3, 6, 9) //> rs : List[Int] = List(1, 3, 6, 9)
def bracket(n: Int, rs: List[Int]) = {
val (l, r) = rs.span(_ < n)
if (l == Nil || r == Nil)
None
else if (r.head == n)
Some((n, n))
else
Some((l.last, r.head))
}
bracket(0, rs) //> res0: Option[(Int, Int)] = None
bracket(2, rs) //> res1: Option[(Int, Int)] = Some((1,3))
bracket(6, rs) //> res2: Option[(Int, Int)] = Some((6,6))
bracket(10, rs) //> res3: Option[(Int, Int)] = None
Alternative if you know the edge cases can't happen:
def bracket(n: Int, rs: List[Int]) = {
val (l, r) = rs.span(_ < n)
if (r.head == n)
(n, n)
else
(l.last, r.head)
}
bracket(2, rs) //> res0: (Int, Int) = (1,3)
bracket(6, rs) //> res1: (Int, Int) = (6,6)
(will throw an exception if there is no lower and upper bound for n)
If you can't have edge cases and you are OK with a tuple that is (<=, >) then simply
def bracket(n: Int, rs: List[Int]) = {
val (l, r) = rs.span(_ <= n)
(l.last, r.head)
}
bracket(2, rs) //> res0: (Int, Int) = (1,3)
bracket(6, rs) //> res1: (Int, Int) = (6,9)
You can use grouped or slide iterators to take 2 elements at a time and test condition:
// you can also add 'numbers.sorted' if your list is not sorted
def findBoundries(x: Int, numbers: List[Int]): Option[List[Int]] =
numbers.grouped(2).find {
case a :: b :: Nil => a <= x && x <= b
}
findBoundries(2,List(1,3,6,9))
You can, as a workaround, also convert your List to a NavigableSet (Java class that has higher and lower methods, which do more or less what you require).

Multiple yields in sequence comprehension?

I'm trying to learn Scala and tried to write a sequence comprehension that extracts unigrams, bigrams and trigrams from a sequence. E.g., [1,2,3,4] should be transformed to (not Scala syntax)
[1; _,1; _,_,1; 2; 1,2; _,1,2; 3; 2,3; 1,2,3; 4; 3,4; 2,3,4]
In Scala 2.8, I tried the following:
def trigrams(tokens : Seq[T]) = {
var t1 : Option[T] = None
var t2 : Option[T] = None
for (t3 <- tokens) {
yield t3
yield (t2,t3)
yield (t1,t2,Some(t3))
t1 = t2
t2 = t3
}
}
But this doesn't compile as, apparently, only one yield is allowed in a for-comprehension (no block statements either). Is there any other elegant way to get the same behavior, with only one pass over the data?
You can't have multiple yields in a for loop because for loops are syntactic sugar for the map (or flatMap) operations:
for (i <- collection) yield( func(i) )
translates into
collection map {i => func(i)}
Without a yield at all
for (i <- collection) func(i)
translates into
collection foreach {i => func(i)}
So the entire body of the for loop is turned into a single closure, and the presence of the yield keyword determines whether the function called on the collection is map or foreach (or flatMap). Because of this translation, the following are forbidden:
Using imperative statements next to a yield to determine what will be yielded.
Using multiple yields
(Not to mention that your proposed verison will return a List[Any] because the tuples and the 1-gram are all of different types. You probably want to get a List[List[Int]] instead)
Try the following instead (which put the n-grams in the order they appear):
val basis = List(1,2,3,4)
val slidingIterators = 1 to 4 map (basis sliding _)
for {onegram <- basis
ngram <- slidingIterators if ngram.hasNext}
yield (ngram.next)
or
val basis = List(1,2,3,4)
val slidingIterators = 1 to 4 map (basis sliding _)
val first=slidingIterators head
val buf=new ListBuffer[List[Int]]
while (first.hasNext)
for (i <- slidingIterators)
if (i.hasNext)
buf += i.next
If you prefer the n-grams to be in length order, try:
val basis = List(1,2,3,4)
1 to 4 flatMap { basis sliding _ toList }
scala> val basis = List(1, 2, 3, 4)
basis: List[Int] = List(1, 2, 3, 4)
scala> val nGrams = (basis sliding 1).toList ::: (basis sliding 2).toList ::: (basis sliding 3).toList
nGrams: List[List[Int]] = ...
scala> nGrams foreach (println _)
List(1)
List(2)
List(3)
List(4)
List(1, 2)
List(2, 3)
List(3, 4)
List(1, 2, 3)
List(2, 3, 4)
I guess I should have given this more thought.
def trigrams(tokens : Seq[T]) : Seq[(Option[T],Option[T],T)] = {
var t1 : Option[T] = None
var t2 : Option[T] = None
for (t3 <- tokens)
yield {
val tri = (t1,t2,t3)
t1 = t2
t2 = Some(t3)
tri
}
}
Then extract the unigrams and bigrams from the trigrams. But can anyone explain to me why 'multi-yields' are not permitted, and if there's any other way to achieve their effect?
val basis = List(1, 2, 3, 4)
val nGrams = basis.map(x => (x)) ::: (for (a <- basis; b <- basis) yield (a, b)) ::: (for (a <- basis; b <- basis; c <- basis) yield (a, b, c))
nGrams: List[Any] = ...
nGrams foreach (println(_))
1
2
3
4
(1,1)
(1,2)
(1,3)
(1,4)
(2,1)
(2,2)
(2,3)
(2,4)
(3,1)
(3,2)
(3,3)
(3,4)
(4,1)
(4,2)
(4,3)
(4,4)
(1,1,1)
(1,1,2)
(1,1,3)
(1,1,4)
(1,2,1)
(1,2,2)
(1,2,3)
(1,2,4)
(1,3,1)
(1,3,2)
(1,3,3)
(1,3,4)
(1,4,1)
(1,4,2)
(1,4,3)
(1,4,4)
(2,1,1)
(2,1,2)
(2,1,3)
(2,1,4)
(2,2,1)
(2,2,2)
(2,2,3)
(2,2,4)
(2,3,1)
(2,3,2)
(2,3,3)
(2,3,4)
(2,4,1)
(2,4,2)
(2,4,3)
(2,4,4)
(3,1,1)
(3,1,2)
(3,1,3)
(3,1,4)
(3,2,1)
(3,2,2)
(3,2,3)
(3,2,4)
(3,3,1)
(3,3,2)
(3,3,3)
(3,3,4)
(3,4,1)
(3,4,2)
(3,4,3)
(3,4,4)
(4,1,1)
(4,1,2)
(4,1,3)
(4,1,4)
(4,2,1)
(4,2,2)
(4,2,3)
(4,2,4)
(4,3,1)
(4,3,2)
(4,3,3)
(4,3,4)
(4,4,1)
(4,4,2)
(4,4,3)
(4,4,4)
You could try a functional version without assignments:
def trigrams[T](tokens : Seq[T]) = {
val s1 = tokens.map { Some(_) }
val s2 = None +: s1
val s3 = None +: s2
s1 zip s2 zip s3 map {
case ((t1, t2), t3) => (List(t1), List(t1, t2), List(t1, t2, t3))
}
}