I have the following snippet of scala code:
def append(as: List[Int], bs: List[Int]): List[Int] = as match {
case Nil => bs
case x::xs => x::append(xs, bs)
}
I don't understand the x:: in the line where x::append(xs, bs) is. I know that when the list as is empty then bs will be returend (with an appended as when as was not empty before). But how does scala know in the mentioned line that is should append as to bs with x::(..,..)
Perhaps it would help if we desugar append a bit
def append(as: List[Int], bs: List[Int]): List[Int] =
as match {
case Nil => bs
case ::(x, xs) =>
val list = append(xs, bs)
list.::(x)
}
Note how :: appears twice, however the first one in
case ::(x, xs)
is actually a case class, even though its symbolic name seems strange, and its declaration looks a bit like this
case class :: (head: A, next: List[A])
whilst, on the other hand, the second :: in
list.::(x)
is actually a right-associative method which Adds an element at the beginning of this list and looks something like so
def :: (elem: B): List[B]
Note how x :: list is equivalent to list.::(x) and not x.::(list) meaning :: is invoked on the right argument.
Related
Specifically:
scala> def f(n: Seq[Any]) = n match {
case Nil => "Empty"
case h :: t => "Non-empty"
}
f: (n: Seq[Any])String
scala> f(Stream())
res1: String = Empty
scala> f(List(1))
res17: String = Non-empty
scala> f(Stream(1))
scala.MatchError: Stream(1, ?) (of class scala.collection.immutable.Stream$Cons)
at .f(<console>:13)
... 33 elided
There are a lot of other ways to implement this, but at written the code was statically safe and failed at run time. What's going on?
For Stream, the concat symbol should be #::, the pattern match should like:
def f(n: Seq[Any]) = n match {
case Nil => "Empty"
case h :: t => "Non-empty"
case h #:: t => "Non-empty stream"
}
for :: is for List / Seq type(List extends from Seq:) ), see:
final case class ::[B](override val head: B, private[scala] var tl: List[B]) extends List[B] {
You can only use :: to deconstruct a List and not a Stream. Since the Stream you provide does not match a List, you get a MatchError. If you want f to support streams using that type of matching (extractors), you can use #::.
def f(n: Seq[Any]) = n match {
case Nil => "Empty"
case h :: t => "Non-empty"
case h #:: t => "Non-empty"
}
In general, this approach is very fragile because both extractor types shown above will only work for those two types of Seq. Others maybe break. If all you care about is determining whether or not the Seq is empty or not, then simply use n.nonEmpty or n.isEmpty and deal with the Boolean result. Otherwise, trying to provide an exhaustive match on a trait that is not sealed is bound to fail.
You can also use the Seq extractor:
def f(n: Seq[Any]) = n match {
case Nil => "Empty"
case Seq(_*) => "Non-empty"
}
While other answers show correctly an extractor specific for Stream, which is #::, there exists an extractor for Seq (and anything derived from SeqLike) - it is +:, this works for both List (as ::) and Stream (as `#::). With this extractor you can easily write a function which works with both:
def f(n: Seq[Any]) = n match {
case h +: t => "Non-empty"
case _ => "Empty"
}
See also Scala pattern matching on sequences other than Lists
I have a code in scala that I wrote but I would like to eliminate ++ from it. The code runs fine but I'm trying not to use ++ or any built in functions or operators (not even :::).
Is it just better to create a val, or are there better ways around it.
Here's a sample of that code.
def partitionAux[A](f: A => Boolean, lst: List[A],
rslt: (List[A], List[A]) ):(List[A], List[A]) = {
lst match {
case Nil => return result
case head :: rest => {
if (f(head))
return partitionHelper(f, rest, (result._1 ++ List[A](head), rslt._2))
else ...
Given that you have only a single element as a second arg of ++, you could implement it yourself:
def concat[A](lst: List[A], a: A): List[A] =
lst match {
case Nil => a :: Nil
case head :: tail => head :: concat(tail, a)
}
println(concat(1 :: 2 :: 3 :: 4 :: Nil, 100)) // 1 :: 2 :: 3 :: 4 :: 100
Also given that you mentioned that partitionAux should split the list into two, it sounds like you should have just head :: result._1, instead of result._1 ++ List[A](head), and then reverse the list in the end. It is easy to write the reverse function yourself, like:
#tailRec
def reverse[A](list: List[A], result: List[A] = Nil): List[A] =
list match {
case Nil => result
case head :: tail => reverse(tail, head :: result)
}
P.S. And you don't need to put return keyword in scala. It doesn't do anything in you case
I'm working on question P07 of Ninety-Nine Scala Problems:
P07 (**) Flatten a nested list structure.
Example:
scala> flatten(List(List(1, 1), 2, List(3, List(5, 8))))
res0: List[Any] = List(1, 1, 2, 3, 5, 8)
My initial attempt at a solution was:
def flatten[A](ls : List[A]): List[A] = {
def flattenRec[A](ls: List[A], flatList: List[A]): List[A] = ls match {
case Nil => flatList
case head: List[A] :: tail => flattenRec(head ::: flatten(tail), flatList)
case head :: tail => flattenRec(tail, flatList :+ head)
}
flattenRec(ls, List[A]())
}
however this doesn't compile as I'm not allowed to specify a type for head in the second case statement. Is there a way for me to do this?
As an aside, the recommended solution uses flatMap instead of match, but I'm not sure why it even needs pattern matching in that case...
You can just bracket the declaration of head:
def flatten[A](ls : List[A]): List[A] = {
def flattenRec[A](ls: List[A], flatList: List[A]): List[A] = ls match {
case Nil => flatList
case (head: List[A]) :: tail => flattenRec(head, flatList)
case head :: tail => flattenRec(tail, flatList :+ head)
}
flattenRec(ls, List[A]())
}
Note that you will get a warning about the type pattern being unchecked (because the fact that head needs to be a list of A and not anything else will be lost at runtime due to erasure), which you will need to assure yourself you can ignore (or run through some hijinks involving TypeTags).
Nice problem and an alternate solution below:
def flatten(a:List[Any]):List[Any] =
a.flatMap{
case l:List[Any] => flatten(l)
case l => List(l)
}
I am new to Scala and try to focus on more functional programming
side of it.
I am trying to write a function filter.
Here is a definition and implementation:
def filter[A, B](f: A => Boolean, l: List[A]): List[B] = l match {
case Nil => List()
case x :: xs => if (f(x)) (x: B) :: filter(f, xs) else filter(f, xs)
}
Using this function, I get this error:
:32: error: type mismatch;
found : x.type (with underlying type A)
required: B
case x :: xs => if (f(x)) (x: B) :: filter(f, xs) else filter(f, xs)
Now, before that I wrote a function map:
def map[A, B](f: A => B, l: List[A]): List[B] = l match {
case Nil => List()
case x :: xs => f(x) :: map(f, xs)
}
that actually applies function f to convert each list element from type A to type B.
My error for filter seems to be caused by the fact that function taken by filter
does nothing to list element x of type A, so type checker thinks that I am still building a list of type A when B is required. You can see I tried to specify x as type B but it was in vain. Can anyone confirm that I understood problem correctly and how can I make x as type B? I could just return list of type A but
this is a function definition that is given as an exercise and I cannot change it.
Your filter method seems to try converting A to B which is not what filter methods usually do. A and B are two unrelated types, so you cannot cast between them
You would want something like that:
def filter[A](l: List[A])(f: A => Boolean): List[A] = l match {
case Nil => List()
case x :: xs => if (f(x)) x :: filter(xs)(f) else filter(xs)(f)
}
But you also can write this one a bit simpler:
def filter[A](l: List[A])(f: A => Boolean): List[A] =
for (x <- l if f(x)) yield x
Please also notice that I have swapped the arguments and divided them. This is done in order for scala's type inference to infer A automatically. Now you can do this:
filter(List(1, 3, 5, 7))(_ > 4)
where Int type is infered implicitly
If the last statement in a function is func(x,tailList):
def func(x:Int):List[Int]...
case head :: tailList => head :: func(x,tailList)
converting this function to tail recursion requires accumulator to be added as a third parameter (and to keep it clean adding a local function inside func() ).
insertTail(x,tailList,head::acc)
doesn't seem to work correctly. shouldn't "acc" hold the computation in progress?
Am I missing something here to make it tail recursive work with accumulator?
Adding a more complete example
def funcTailTest(x:Int,xs:List[Int]):List[Int] = {
#tailrec
def inner(x:Int,xs:List[Int],acc:List[Int]) : List[Int] = xs match {
case head::tailList => {
inner(x,tailList,head::acc)
}
}
inner(x,xs,Nil)
}
basically head should be added to the output of inner() function so w/o an attempt to make it tail recursive last statement in a case will look
head::inner(x,tailList)
Assuming that reverse is also tail recursively implemented (which it definitely can be), the following is a tail recursive append:
def append[T](y: T, xs: List[T]): List[T] = {
#tailrec
def appendAcc[T](y: T, xs: List[T], acc: List[T]): List[T] = xs match {
case Nil => y :: acc
case x :: xs => appendAcc(y, xs, x :: acc)
}
appendAcc(y, xs, Nil).reverse
}
Following is a scala function to implement Tail recursion using cons operator
def reverseUtility(ele: List[Int], res: List[Int]):List[Int] ={
ele match {
case Nil => Nil
case x :: Nil => x :: res
case x :: y => reverseUtility(y, (x::res))
}}
Pass a list of Int and an empty list called res(result) as parameters for the function. Time complexity for the function is O(N)