How to perform pattern matching with vararg case classes?
I have a set of case classes like this
abstract class Shape
case class Rectangle(width: Int, height: Int) extends Shape
case class Location(x: Int, y: Int, shape: Shape) extends Shape
case class Circle(radius: Int) extends Shape
case class Group(shape: Shape*) extends Shape
where basically Group is an array of shapes. I need to define a size method for computing sizes for rectangle, circle and location its straightforward just return one. But i am having difficulty for Group.
object size extends Shape{
def size(s: Any) : Int = s match {
case Rect开发者_如何学Goangle(x,y) => 1
case Group // how to do it? Also having case Group(shape : Shape*) gives an error
case Circle(r) => 1
case Location(x,y,shape) => 1
}
}
I know for Group i need to use map and fold left, but i really cant create a logic for it. Thanks
Either of these will work, the second is probably preferred if a little weird at first glance. See 8.1.9 Pattern Sequences from the Scala Reference.
case g: Group => g.shape.map(size(_)).sum
case Group(ss @ _*) => ss.map(size(_)).sum
This is using Scala 2.8. sum
may not work on older versions.
The syntax for vararg pattern matching is somewhat strange.
def size(s: Shape) : Int = s match{
case Rectangle(x,y) => 1
case Circle(r) => 1
case Location(x,y,shape) => 1
case Group(shapes @ _*) => (0 /: shapes) { _ + size(_) }
}
Note that in the last line, you sum up the sizes of all sub-shapes
starting with zero using the /:
-notation for folds.
How folds work: Folds accumulate the elements of a sequence using a given function.
So in order to compute the sum of a list, we would write (Haskell-style)
fold (\total element -> total + element) 0 list
which would combine all elements of the list with the given addition function starting with 0 (and therefore compute the sum).
In Scala, we can write it this way:
(0 /: list) { (total, element) => total + element }
which can be simplified to
(0 /: list) { _ + _ }
The first step is figuring out what you mean. The two most obvious choices are the total area covered by all the shapes, and the minimum rectangle containing them all. If for circles you return the actual area, they you probably have to go with the actual area.
There's no closed-form way to answer this. I might consider throwing a thousand random darts at a minimum enclosing rectangle and estimating the area as the percentage of darts that hit an occupied point. Is an estimate an acceptable response?
Are you guaranteed that all the shapes will be circles and rectangles? You might be able to cobble together a solution that would work for them. If Shapes might be extended further, then that won't work.
For Location size should drill down to get size since shape could be group which causes a higher count
case Location(x,y,shape) => size(shape)
That is if size is the number of shapes in the Shape
case g: Group => g.shape.map(size(_)).sum
case Group(ss @ *) => ss.map(size()).sum
both of these gives the error value sum is not a member of Seq[Int]
However this oen works
case Group(shapes @ _*) => (0 /: shapes) { _ + size(_)
精彩评论