views:

412

answers:

5

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 Rectangle(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

+5  A: 

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) { _ + _ }
Dario
This doesn't account for overlaps. Does that matter?
PanCrit
can you please explain me how the last line works its way too strange????? case Group(shapes @ _*) => (0 /: shapes) { _ + size(_) }
tom
@PanCrit: I'm not that fluent in Scala - Could you explain what's meant with *overlap*, please?
Dario
@tom The `@` symbol means "assign to the identifier on my left whatever was matched by the pattern on my right". For example, `r @ Rectangle(x, y)` will assign the whole `Rectangle` to `r`, as well as assigning its decomposition to `x` and `y`. Now, the pattern `_*` just means "any number of whatever".
Daniel
@Dario I suppose PanCrit is saying that you can't just add the size of all shapes, because they might be overlapping. The size of the union would, then, be less than the sum of all sizes.
Daniel
That's what I meant, Daniel. Overlap isn't a Scala concept, it's a geometry concept.
PanCrit
@PanCrit, Daniel: Ah well, okay (thought it refers to some kind of overlapping patterns like they exist in Haskell). A geometric explanation of course makes sence although I don't really get the meaning of `size` here? Should it be an area? Why is it constant `1`? Anyway, just see my answer as a pattern-matching answer with example fold ;)
Dario
+6  A: 

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.

retronym
This doesn't account for overlaps. Is that important?
PanCrit
My assumption is that the question is about pattern matching in Scala rather than geometry.
retronym
+1  A: 

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.

PanCrit
A: 

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

tom
Please do not post questions as answers but edit your question!
Dario
A: 

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

Matt W