2

I have the following structs that represent a point or a line:

public struct Point{
    let x : Double
    let y : Double

    init (x : Double, y : Double)
    {
        self.x = x
        self.y = y
    }
}
extension Point : Equatable{}
    public func ==(lhs: Point, rhs: Point) -> Bool
    {
        return lhs.x == rhs.x && lhs.y == rhs.y
    }

And

public struct Line {
    let points : [Point]
    init(points : [Point])
    {
        self.points = points
    }
}

extension Line : Equatable {}
    public func ==(lhs: Line, rhs: Line) -> Bool
    {
        return lhs.points == rhs.points
    }

I want to be able to have a Shape protocol or struct that I can use to have Points and Lines and then I can compare between them. I tried to that with conforming protocol Shape but Swift compiler gives me an error when I want to compare a point with a line even though they are Shapes.

Do I have to move from struct to classes?
I think I may have to use generics but don't know exactly how to solve this issue. Thanks in advance for any guidance.

Edit1:

My approach to Shape protocol was really just trying stuff but nothing worked. I tried the following:

protocol MapShape : Equatable
{
      func == (lhs: MapShape, rhs: MapShape ) -> Bool
}

I also changed the code for the Equatable extension for lines given the suggestion

Franklin
  • 881
  • 1
  • 8
  • 28
  • If I remember correctly, the WWDC 2015 video on "Swift Protocol Oriented Programming" covers this issue. (In any case, every Swift programmer should have seen that!) – Martin R Jul 21 '15 at 18:48
  • What was your attempt at the shape struct? Please share it – Kametrixom Jul 21 '15 at 18:51
  • Shared the protocol I tried to use and edited my code given your suggestion @Kametrixom – Franklin Jul 21 '15 at 18:56
  • @Franklin I edited my answer, have another look, also a line just has a starting point and an end point, not an array of points, I edited my code like this as well – Kametrixom Jul 21 '15 at 18:58
  • @Kametrixom true. I am modeling a Polyline... I need it to be as a collection of points. But your observation was really valid. Tks – Franklin Jul 21 '15 at 18:59
  • Oh god i edited your question accidentaly, please mods or whatever reject my edit, pleassseeee Im so sorry, I wanted to edit my own answer but missclicked (you can't cancel edits that's why) – Kametrixom Jul 21 '15 at 19:15

2 Answers2

6

This topic is covered in the WWDC 2015 session video Protocol-Oriented Programming in Swift, and here is my attempt to apply that to your situation:

You define a protocol Shape and a protocol extension method isEqualTo::

protocol Shape {
    func isEqualTo(other: Shape) -> Bool
}

extension Shape where Self : Equatable {
    func isEqualTo(other: Shape) -> Bool {
    if let o = other as? Self { return self == o }
    return false
    }
}

isEqualTo: checks if the other element is of the same type (and compares them with == in that case), and returns false if they are of different type.

All types which are Equatable automatically conform to Shape, so that we can just set

extension Point : Shape { }
extension Line : Shape { }

(Of course you can add other methods to Shape which should be implemented by all shape types.)

Now we can define == for shapes as

func ==(lhs: Shape, rhs: Shape) -> Bool {
        return lhs.isEqualTo(rhs)
}

and voilà, points and lines can be compared:

let p1 = Point(x: 1, y: 2)
let p2 = Point(x: 1, y: 3)
let l1 = Line(points: [p1, p2])
let l2 = Line(points: [p1, p2])

print(p1 == p2) // false
print(p1 == l1) // false
print(l1 == l2) // true

Remark: You have == for the Shape type now, but I haven't figured out yet to how make Shape conform to Equatable. Perhaps someone else can solve that part (if it is possible).

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • I understand that Shape does not conforms to Equatable but this solution will work for my case! Thanks – Franklin Jul 21 '15 at 19:44
1

Please allow me to shorten and correct your code a bit:

struct Point : Equatable {
    let x : Double
    let y : Double
}

func ==(lhs: Point, rhs: Point) -> Bool {
    return lhs.x == rhs.x && lhs.y == rhs.y
}

struct Line : Equatable {
    let a : Point
    let b : Point
}

func ==(lhs: Line, rhs: Line) -> Bool {
    return lhs.a == rhs.a && lhs.b == rhs.b
}

I think I know now what you want:

protocol Shape : Equatable {

}


struct Polygon : Shape {
    let points : [Point]
}

func ==(lhs: Polygon, rhs: Polygon) -> Bool {
    return lhs.points == rhs.points
}



struct Circle : Shape {
    let center : Point
    let radius : Double
}

func ==(lhs: Circle, rhs: Circle) -> Bool {
    return lhs.center == rhs.center && lhs.radius == rhs.radius
}

Note: You have to implement == for every shape on its own, because every shape type has different properties

If you want every Shape to have a certain method, you can just add the requirement to the protocol:

protocol Shape : Equatable {
    func move(dx: Double, dy: Double) -> Self
}

and make every Shape conform to it:

struct Polygon : Shape {
    let points : [Point]

    func move(dx: Double, dy: Double) -> Polygon {
        return Polygon(points: points.map { Point(x: $0.x + dx, y: $0.y + dy) })
    }
}

struct Circle : Shape {
    let center : Point
    let radius : Double

    func move(dx: Double, dy: Double) -> Circle {
        return Circle(center: Point(x: center.x + dx, y: center.y + dy), radius: radius)
    }
}
Kametrixom
  • 14,673
  • 7
  • 45
  • 62