Given the following list:
sealed abstract class IntList
case class Empty() extends IntList
case class Element(n: Int, tail: IntList) extends IntList
Define the function drop(n, xs).
It should return the list xs, without the first n elements.
This is what I tried:
def drop(n: Int, xs: IntList): IntList = xs match {
case _ if n == 0 => xs
case xs : Empty => Empty()
case xs : Element => Element(xs.tail.n, drop(n-1, xs.tail))
}
but
error: value n is not a member of Solution.IntList
case xs : Element => Element(xs.tail.n, drop(n-1, xs.tail))
I am guessing this is because xs.tail is not guaranteed to be an Element anymore
How should I do this? Thanks.
Nearly there, but there's no need to wrap the recursive call in Element()
def drop(n: Int, xs: IntList): IntList = xs match {
case _ if n == 0 => xs
case xs : Empty => xs
case xs : Element => drop(n-1, xs.tail)
}
This avoids the need to call xs.tail.n, which you already identified as the problem!
You can also just return xs for the Empty case, to avoid creating a new instance. Usually one would use a case object for an empty case like this.
Related
I understand (kind of) how pattern matching works in Scala,
Let's say I have two lists of the form:
sealed abstract class IntList
case class Empty() extends IntList // The empty list, often called Nils
case class Element(n: Int, tail: IntList) extends IntList // Element is usually called Cons
Let's say I want to create the function take(n, xs)
It should return the first n elements of xs.
I tried with normal pattern matching:
def take(n: Int, xs: IntList): IntList = xs match {
case n == 0 => Empty()
case xs : Empty => Empty()
case xs : Element => Element(xs.n, take(n-1, xs))
}
But then of course, n is not recognised, error: not found: value == case n == 0 => Empty()
How can I do this, it is probably simple but I am a beginner in Scala?
You have a typo in the first case where it should be
case _ if n == 0 => Empty()
and a bug in third case where you forgot to pass the tail
case xs: Element => Element(xs.n, take(n-1, xs.tail))
Try
sealed trait IntList
case object Empty extends IntList
case class Element(n: Int, tail: IntList) extends IntList
def take(n: Int, xs: IntList): IntList = xs match {
case _ if n == 0 => Empty
case Empty => Empty
case Element(n, tail) => Element(n, take(n-1, tail))
}
val list = Element(1, Element(2, Element(3, Empty)))
take(2, list) // res0: IntList = Element(1,Element(2,Empty))
Consider case object instead of case class Empty() when there is no data as per Differences between case object T and case class T() when defining ADT?
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 list of elements, and want to group them by class and then process the results.
trait H
case class A(x: Int) extends H
case class B(x: Int) extends H
val x = List(A(1),B(2),B(3))
val y = x.groupBy(_.getClass)
y.map(_ match {case (A, alist) => println("found me some As")
case (B, blist) => println("not As")})
Unfortunately this produces errors like:
<console>:17: error: pattern type is incompatible with expected type;
found : A.type
required: Class[_ <: Product]
Note: if you intended to match against the class, try `case _: A`
y.map(_ match {case (A, alist) => println("found me some As")
That is, I can't seem to find the proper way to do case matching when the item being matched is a class not just an instance of one.
A partial solution is the following:
val z = y.map(_ match {case (atype, alist) => alist })
z.map(_ match {
case alist if alist.head.isInstanceOf[A] => alist
case blist => List()
})
But it feels like there should be a better way of doing this using the keys to the initial Map returned from groupBy.
A in case (A, alist) refers to the companion object of A, which has the type A.type. That's why you're getting that error message. If you want to match against the class meta data, you should refer to classOf[A]. There's also no reason to use match, as you can pattern match with map already.
y.map {
case (c, alist) if(c == classOf[A]) => println("found me some As")
case (c, blist) if(c == classOf[B]) => println("not As")
}
When pattern matching a list, it seems common to return an empty list when given an empty list. We can match an empty list to Nil or List(), but we can return empty as Nil, List() or by returning the given list argument itself.
What's the convention here?
When would you choose one method over another?
Examples:
def givenEmptyNumsReturnsNil(nums: List[Int]): List[Int] = nums match {
case List() => Nil
case x :: xs => ???
}
def givenEmptyNumsReturnsEmptyList(nums: List[Int]): List[Int] = nums match {
case List() => List()
case x :: xs => ???
}
def givenEmptyNumsReturnsNums(nums: List[Int]): List[Int] = nums match {
case List() => nums
case x :: xs => ???
}
I'm scala beginner and don't know any existing convention about it. My things about it:
The last one isn't intuitive
I prefer return that I match. If I have case List(), so I return List().
You can also match Nil:
-
def givenEmptyNumsReturnsNil(nums: List[Int]): List[Int] = nums match {
case Nil => Nil
case x :: xs => ???
}
But Nil and List() are the same.
For choice better way, just clarify what inside:
case N1:
def givenEmptyNumsReturnsEmptyList(nums: List[Int]): List[Int] = nums match {
case List() => List()
case x :: xs => ???
}
Will call unaplay method from object List, after will call apply method of object List.
case N2:
def givenEmptyNumsReturnsNil(nums: List[Int]): List[Int] = nums match {
case Nil => Nil
case x :: xs => ???
}
Will compare value before match with object Nil and will return object Nil
And in case of choice I prefer case N2 because it is little bit optimal.
I am trying to do some examples programs in scala to get more familiar with the language, For that I am trying to re-implement some of the built in methods in Haskell, Most of these methods I am sure are also implemented in Scala too, But these are just for my practice. I think I can post some of code snippets (not all of them) to get a better way of doing things and to validate my understanding of scala. So please let me know if this is not the place to do these things.
Here is my scala implementation to get the last element of any list. Is this the right way of doing things, By using Any am I loosing the type of the object containing in the list? Is this how this kind of things implemented in scala?
def getLast(xs: List[Any]): Any = xs match {
case List() => null
case x :: List() => x
case _ :: ys => getLast(ys)
}
Parameterize the type of your function and use "Nil" instead of List() like so:
def getLast[T](xs: List[T]): T = xs match {
case Nil => null.asInstanceOf[T]
case x :: Nil => x
case _ :: ys => getLast(ys)
}
Also, consider making it return an Option type:
def getLast[T](xs: List[T]): Option[T] = xs match {
case Nil => None
case x :: Nil => Some(x)
case _ :: ys => getLast(ys)
}
Usage:
val listOfInts = List(1,2,3)
assert(getLast(listOfInts).isInstanceOf[Int])
val listOfStrings = List("one","two","three")
assert(getLast(listOfStrings).isInstanceOf[String])
Firstly, avoid the null, and especially null.asInstanceOf[T]. Observe the danger with primitives:
scala> null.asInstanceOf[Int]
res19: Int = 0
scala> null.asInstanceOf[Boolean]
res20: Boolean = false
So the signature should either be List[T] => T, whereby last on an empty iterator throws an exception:
def last[T](ts: List[T]): T = ts match {
case Nil => throw new NoSuchElementException
case t :: Nil => t
case t :: ts => last(ts)
}
Or instead: List[T] => Option[T]
def lastOption[T](ts: List[T]): Option[T] = ts match {
case Nil => None
case t :: Nil => Some(t)
case t :: ts => lastOption(ts)
}
def lastOption1[T](ts: List[T]): Option[T] = ts.reverse.headOption
def lastOptionInScala28[T](ts: List[T]): Option[T] = ts.lastOption // :)