kb84tkhrのブログ

何を書こうか考え中です あ、あと組織とは関係ないってやつです 個人的なやつ

16章 計算幾何学 (続き3)

この間

>>> greeting(1)
TypeError: must be str, not int

書いただけで使えるみたい

って書いたけど完全に勘違いだな
これはただのランタイムエラーだ

いろいろ見たり試したりして
Polygon = List[Point]って書けることはわかったけど
結局Polygonクラスを作ることになりそうな気もしている

などなど思いつつ続き
Pointの演算子を定義する

double x, y;
Point operator + ( Point &p ) {
    return Point(x + p.x, y + p.y)
}

なにこのxとy
C++演算子オーバーロードをしてるんだろうけど
class Point { ... }を省略して書いてるってことかな
まあいいや言いたいことはわかる

class Point:

    ...

    def __add__(self, other: 'Point') -> 'Point':
        return Point(self.x + other.x, self.y + other.y)

こうですね

$ python3 -i shape.py
>>> Point(1, 2) + Point(3, 4)
Point(4, 6)

引き算にかけ算

    def __sub__(self, other: 'Point') -> 'Point':
        return Point(self.x - other.x, self.y - other.y)

    def __mul__(self, k: float) -> 'Point':
        return Point(self.x * k, self.y * k)

これだとPoint(1, 2) * 2とは書けても
2 * Point(1, 2)とは書けないな

>>> Point(1, 2) * 2
Point(2, 4)
>>> 2 * Point(1, 2)
TypeError: unsupported operand type(s) for *: 'int' and 'Point'

なんかあった気がする
こうだ

    def __rmul__(self, k: float) -> 'Point':
        return self * k

どうだ

>>> 2 * Point(1, 2)
Point(2, 4)

できた

次はノルムと大きさ
サンプルは関数で書いてあるけど、Pointクラスのメソッドにしよう
特に難しいところはない

Point全体のサンプルと見比べて__eq__を書き換え、
__truediv____lt__を追加
__lt__ってこんなふうに定義するんだ・・・と思ったけど

bool operator < (const Point &p) const {
    return x != p.x ? x < p.x : y < p.y
}

==を差がEPSより小さい、ってやるんだったらここもそうすべきでは?
などと考えつつPointクラスはこうなりました

class Point:

    EPS = 1e-10

    def __init__(self, x: float=0.0, y: float=0.0) -> None:
        self.x = x
        self.y = y

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Point):
            return NotImplemented
        return abs(self.x - other.x) < Point.EPS and \
            abs(self.y - other.y) < Point.EPS

    def __add__(self, other: 'Point') -> 'Point':
        return Point(self.x + other.x, self.y + other.y)

    def __sub__(self, other: 'Point') -> 'Point':
        return Point(self.x - other.x, self.y - other.y)

    def __mul__(self, k: float) -> 'Point':
        return Point(self.x * k, self.y * k)

    def __rmul__(self, k: float) -> 'Point':
        return self * k

    def __truediv__(self, k: float) -> 'Point':
        return Point(self.x / k, self.y / k)

    def __lt__(self, other: 'Point') -> bool:
        return self.y < other.y \
            if abs(self.x - other.x) < Point.EPS \
            else self.x < other.x

    def __repr__(self) -> str:
        return "Point({}, {})".format(self.x, self.y)

    def norm(self):
        return self.x * self.x + self.y * self.y

    def abs(self):
        return sqrt(self.norm())