开发者

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(_)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜