I have been trying to learn pattern matching and pairs in Scala and use it to implement the merge sort but the pattern matching. But the pattern match to extract head and tail pair doesn't work. What is it which I am missing in the below code?
def merge(xs: List[Int], ys: List[Int]): List[Int] =
(xs, ys) match {
case (x: Int, y: Int) :: (xs1: List[Int], ys1: List[Int]) =>
if (x < y) x :: merge(xs1, ys)
else y :: merge(xs, ys1)
case (x: List[Int], Nil) => x
case (Nil, y: List[Int]) => y
}
you have a syntax error in your 1st case statement, change it to
case (x :: xs1 , y :: ys1)
from
case (x: Int, y: Int) :: (xs1: List[Int], ys1: List[Int])
You are trying to match a tuple containing Lists, not List of Tuples
Related
I want to remove all instances of an element from a list, like this:
def remove(x: Char, xs: List[Char]) = xs match {
case Nil => Nil
case x :: ys => remove(x, ys)
case y :: ys => y :: remove(x, ys)
}
However, I get an error on the line case y :: ys => y :: remove(x, ys) saying Cannot resolve symbol ::
Am I pattern matching correctly?
I got a couple of different compile errors, first: "error: recursive method remove needs result type" (fixed by adding return type of List[Char]), then (as I expected): "warning: unreachable code" on the line with y :: ys. This latter warning comes because the x used in the line with case x :: ys is not recognised as the same x from the function's arguments - it is shadowing the argument variable, and so matched any character.
To be sure to match the function argument, add backticks:
def remove(x: Char, xs: List[Char]): List[Char] = xs match {
case Nil => Nil
case `x` :: ys => remove(x, ys)
case y :: ys => y :: remove(x, ys)
}
or you can capitalise the argument name (capitalised variables in a match case are treated specially this way):
def remove(X: Char, xs: List[Char]): List[Char] = xs match {
case Nil => Nil
case X :: ys => remove(X, ys)
case y :: ys => y :: remove(X, ys)
}
If using this style, don't forget to capitalise the argument everywhere it is referenced!
You need to specify the return type if you are using recursive call. This will work:
def remove(x: Char, xs: List[Char]):List[Char] = xs match {
case Nil => Nil
case x :: ys => remove(x, ys)
case y :: ys => y :: remove(x, ys)
}
I agree, the error is misleading
The first problem is that a recursive method needs to declare its return type.
def remove(x: Char, xs: List[Char]): List[Char] = xs match {
case Nil => Nil
case x :: ys => remove(x, ys)
case y :: ys => y :: remove(x, ys)
}
The second problem is that both cases are the same so the second one is unreachable.
Prof. Odersky in Coursera course has implemented flatMap using for loops as following:
def flatMap[T, U](xs: List[T], f: T => Iterable[U]) = for (x <- xs; y <- f(x)) yield y
And here's some application on the two functions (result as expected):
val xs = List(1, 2, 3, 4, 5)
flatMap[Int, Int](xs, x=> List(x /2))
However, When I try implement the flatMap as:
for (x <- xs) yield f(x)
It does not return the correct answer, it returns a List of Lists (it should return one big list contains all elements of the inner lists)
My question is, What is the different that make flatMap works as expected; between both:
for (x <- xs; y <- f(x)) yield y
//And
for (x <- xs) yield f(x)
And Why for (x <- xs; y <- x * x) yield y not compiling?
Your second version doesn't involve a call to flatMap at all. You can use reify to de-sugar the for comprehension and see:
scala> import reflect.runtime.universe._
import reflect.runtime.universe._
scala> val f: Int => Iterable[Int] = null
f: Int => Iterable[Int] = null
scala> val xs = List(1, 2, 3, 4, 5)
xs: List[Int] = List(1, 2, 3, 4, 5)
scala> reify { for (x <- xs; y <- f(x)) yield y }
res0: reflect.runtime.universe.Expr[List[Int]] = Expr[List[Int]]($read.xs.flatMap(((x) => $read.f.apply(x).map(((y) => y))(Iterable.canBuildFrom)))(List.canBuildFrom))
scala> reify { for (x <- xs) yield f(x) }
res1: reflect.runtime.universe.Expr[List[Iterable[Int]]] = Expr[List[scala.Iterable[Int]]]($read.xs.map(((x) => $read.f.apply(x)))(List.canBuildFrom))
the first can be simplified to:
xs.flatMap(x => f(x).map(identity))
which is equivalent to:
xs.flatMap(f)
and the second can be simplified to:
xs.map(f)
What's wrong with this simple scala code?
val l = List(("a", 1), ("b", 2), ("c", 3), ("d", 4), ("e", 5))
l.fold(0) {(acc: Int, tup: (String, Int)) => acc + tup._2}
:9: error: type mismatch;
found : (Int, (String, Int)) => Int
required: (Any, Any) => Any
l.fold(0) {(acc: Int, tup: (String, Int)) => acc + tup._2}
In other functional languages (e.g., f#) this works:
let l = [("a", 1); ("b", 2); ("c", 3); ("d", 4)];;
List.fold(fun accm tup -> accm + (snd tup)) 0 l;;
val it : int = 10
The fold method assumes an associative operator and can in theory (e.g. when using parallelism) be performed in arbitrary order. The signature thus makes it clear that the accumulating type must be a super-type of the collection's element:
def fold[A1 >: A](z: A1)(op: (A1, A1) ⇒ A1): A1
The inferred super-type of (String, Int) and Int is Any.
This is all described in the API documentation.
What you want is a foldLeft or foldRight which do not have this restriction on the type:
def foldLeft[B](z: B)(f: (B, A) ⇒ B): B
Therefore:
l.foldLeft(0) { (acc, tup) => acc + tup._2 }
or
(0 /: l) { case (acc, (_, n)) => acc + n }
Here is a function I wrote for concatenating elements of a List using an accumulator with tail recursion :
val l1 = List(1, 2, 3) //> l1 : List[Int] = List(1, 2, 3)
val l2 = List(1, 2, 3) //> l2 : List[Int] = List(1, 2, 3)
def func(l1: List[Int], l2: List[Int], acc: List[Int]): List[Int] = {
(l1, l2) match {
case (Nil, Nil) => acc.reverse
case (h1 :: t1, h2 :: t2) => {
func(t1, t2, h1 :: h2 :: acc)
}
}
} //> func: (l1: List[Int], l2: List[Int], acc: List[Int])List[Int]
func(l1, l2, List()) //> res0: List[Int] = List(1, 1, 2, 2, 3, 3)
This is my understanding of the call order
func( 1 :: 1 :: () )
func( 2 :: 2 :: 1 :: 1 : () )
func( 3 :: 3 :: 2 :: 2 :: 1 :: 1 : () )
So the call order is the reason why I must call reverse on base call of acc so that the List is ordered in same ordering initial List elements. To try to minimize the steps required to concatenate the lists I have tried to add the elements like this :
func(t1, t2, acc :: h1 :: h2)
instead of
func(t1, t2, h1 :: h2 :: acc)
but receive compile time error :
value :: is not a member of Int
So it seems I cannot prepend these elements to this List ?
When you write x :: y, y must be a list and x the element you want to prepend.
You can use acc :+ h1 :+ h2 to append h1 and h2 to acc, but note that adding elements to the end of the list is a relatively expensive operation (linear with the length of the list).
Why do both of the following foldLeft's result in the same output?
#1
scala> List(1,2,3).foldLeft(List[Int]())( (acc, el) => acc :+ el)
res114: List[Int] = List(1, 2, 3)
And, now using _ :+ _ as the (B, A) => B argument.
#2
scala> List(1,2,3).foldLeft(List[Int]())(_ :+ _)
res115: List[Int] = List(1, 2, 3)
In particular, the lack of explicitly appending to the accumulator in the second case confuses me.
_ :+ _ is simply a shorthand for (x1, x2) => x1 :+ x2, just as list.map(_.toString) is simply list.map(x => x.toString).
See more on the placeholder syntax here.