acokikoy's notes

最近気になる=[NoCode, Shopify], I am..=[Python, ウクレレ, マニュアル車, CMS] LoveなWebディレクター

毎朝Codewars@2019.07.04(木): Change your Points of View

毎朝ちびちびCodewars。

Change your Points of View [6級] www.codewars.com

今日のお題:

"""
次の4つの関数を定義する。
point(x, y) は、座標(x, y) の情報を含んだ callable な関数を返す。
def point(x, y):
  return lambda ...

callable(point(3,4)) -> True

fst(pt) , snd(pt) は、それぞれ地点(x, y)に対する x, y を返す関数。
lint(pt1, pt2)は、2点間を通る直線の式 ax + by + c = 0 の定数[a, b, c]を返す関数。


例:
    p0 = point(3, 5)
    callable(p0) -> True
    fst(p0) -> 3
    snd(p0) -> 5

    p1 = point(22, 55),
    p2 = point(75, 66)
    sqr-dist(p1, p2) -> 2930

    p3 = point(20, 22)
    p4 = point(29, 10)
    line(p3, p4) -> [12, 9, -438] 

コード

mine

# Change your Points of View
# https://www.codewars.com/kata/change-your-points-of-view/

def point(a, b):
    return lambda : (a, b)

def fst(pt):
    return pt()[0]

def snd(pt):
    return pt()[1]

def sqr_dist(pt1, pt2):
    return (fst(pt2)-fst(pt1))**2 + (snd(pt2) - snd(pt1))**2

def line(pt1, pt2):
    l = snd(pt1) - snd(pt2) 
    m = fst(pt2) - fst(pt1)
    n = fst(pt2) * (snd(pt2) - snd(pt1)) - snd(pt2) * (fst(pt2) - fst(pt1))
    return [l, m, n]

他ユーザによるエレガントな解    

def point(a, b):
    f = lambda: (a, b)
    f.x = a
    f.y = b
    return f
    
def fst(pt):
    return pt.x
    
def snd(pt):
    return pt.y
    
def sqr_dist(pt1, pt2):
    return (pt2.x-pt1.x) ** 2 + (pt2.y-pt1.y) ** 2
    
def line(pt1, pt2):
    a = pt1.y - pt2.y
    b = pt2.x - pt1.x
    c = pt1.x*pt2.y - pt2.x*pt1.y
    return [a, b, c]

point()内で 無名関数のリファレンスを返値で返すところがポイント。
return point では p1 = point(10, 20) p2 = point(1, 2) とかしたら p1 も p2 も同じになっちゃう。

ちょっとわからなかったのは、point()関数内の f.x, f.yのこと。
point関数のローカル変数ではなさそう。

実際に point関数内で、 locals()を実行すると
{'f': <function point.<locals>.<lambda> at 0x10f933950>, 'b': 3, 'a': 2}
が返り f.x, f.yは含まれてない。

無名関数(ラムダ式)のローカル変数??
point()の外から参照もできれば、書き換えることもできる。

>>> p1 = point(20, 30)
>>> print(p1.x)
20
>>> p1.x = 2
>>> print(p1.x)
2

振る舞いとしてはClassで ↓こういう風に書いた時に近いのかな。。。?!

class Point:
    def __init__(self, a, b):
        self.x = a
        self.y = b

なにも関数の中で書かなくてもいいのか。

def point2(a, b):
    f = lambda: (a, b)
    return f

p = point2(2, 3)
p.x = 10
p.y = 20
print(p())              # (2, 3)
print(p.x, p.y)       # 10 20

文法、関数・メソッドnote

callable(object)

組み込み関数 — Python 3.7.4rc2 ドキュメント

今回のチャレンジのテストケースで使われていた組み込み関数。 objectが呼び出し可能なオブジェクトなら True 、そうでなければ False を返す。 今回の例なら

>>> callable(point)      # 関数
True
>>> callable(Point)      # クラス
True
>>> callable(p1.x)        # p1.xはint型のリテラル
False