gopython

gorubyを使うとコードがとても短くなってゴルフが楽という話を聞いたので、Pythonでゴルフを楽にするにはどうすればいいかと考えてみる。徐々に書く。

In [3]: for i in range(10):
   ...:     globals()["s%d"%i] = lambda x,i=i:globals().__setitem__("x%s"%i, x)
   ...: 

In [4]: s1(100)

In [5]: x1
Out[5]: 100

とりあえず式の中でグローバル変数への代入を使えるようにしたけどどう考えても卑怯だ。

        • -
class SetGlobals:
    def __getattr__(self, name):
        def func(value):
            globals().__setitem__(name, value)
            return value
        return func

g = SetGlobals()

print [g.x(2), x] # [2, 2]

これでようやく

[$x=2, $x]

が出来る言語と同じスタートラインに立った感じ。

        • -

gorubyの短縮メソッド名の展開はこんな感じなのかな。

def find_item(namespace, name):
    """
    >>> ns = "size range string stringstring x xs".split()
    >>> ns = dict(zip(ns, ns))
    >>> find_item(ns, "x")
    'x'
    >>> find_item(ns, "r")
    'range'
    >>> find_item(ns, "si")
    'size'
    >>> find_item(ns, "ss")
    'stringstring'
    """
    if name in namespace:
        return namespace[name]
    candidate = [k for k in namespace if k.startswith(name)]
    if len(candidate) == 1:
        return candidate[0]

    # zero or more than one exact matches 
    for can in namespace:
        start = 0
        for c in name:
            start = can.find(c, start) + 1
            if start == 0:
                break
        else:
            return can
    
    raise RuntimeError(candidate)
        • -

っていうかとにかく短く書けるようにするためには、とある魔力の源(マナ)みたいなオブジェクトがあるべきだと思うんだよ。
でもって、そのマナから導きだしたオブジェクトはすべて魔力を帯びるようにするべきだと思うんだよ。

class Mana:
    def __call__(self, v):
        "add magical power"
        if isinstance(v, str):
            return MagicalString(v)
        if isinstance(v, int):
            return MagicalInteger(v)

M = Mana()

class MagicalObject:
    def __init__(self, v):
        self.value = v

class MagicalString(MagicalObject):
    def __add__(self, v):
        if isinstance(v, MagicalObject):
            v = v.value
        if isinstance(v, int):
            return self.value + str(v)
        return self.value + v

class MagicalInteger(MagicalObject):
    def __add__(self, v):
        if isinstance(v, MagicalObject):
            v = v.value
        if isinstance(v, str):
            return self.value + int(v)
        return self.value + v

i = M(100)
s = M("100")
print i + s #=> 200
print s + i #=> 100100

これでようやく "foo" + 100とかが出来るようになった。

        • -

MagicalListは+で破壊的に追加をするようにした。なぜならコピーをとるのは[:]の3バイトでいいから。あとハッシュのキーになれるようにした。

class MagicalList(MagicalObject):
    """
    >>> xs = MagicalList(0, 1, 2)
    >>> xs.value
    [0, 1, 2]
    >>> (xs + 3).value
    [0, 1, 2, 3]
    >>> d = {}
    >>> d[xs] = 0
    >>> d[MagicalList(0,1,2,3)]
    0
    """
    def __init__(self, *value):
        if len(value) == 1:
            self.value = list(value[0])
        self.value = list(value)

    def join(self, x):
        return M(x.join(self.value))

    def __hash__(self):
        return hash(tuple(self.value))

    def __eq__(self, v):
        if isinstance(v, MagicalObject):
            v = v.value
        return self.value == v

    def __add__(self, value):
        """add destructively"""
        if isinstance(value, list) or isinstance(value, tuple):
            self.value.extend(value)
        else:
            self.value.append(value)
        return self

完成にはほど遠いが飽きてきた。全体のコードを見たい人がたくさんいたらCodeReposに入れる。見たい人ははてなスターでも押してみて。