関数合成のススメ 〜 オブジェクト指向プログラマへ捧げる関数型言語への導入その1
関数適用のススメ 〜 オブジェクト指向プログラマへ捧げる関数型言語への導入その2
上のような記事を見つけたので、ああ^〜〜Pythonでもやりたいなあ^〜〜と思ってちょこちょこしてみました。
まずは関数合成用関数の実装から
(g ∘ f)(x) = g(f(x)) らしいので
def compose(f_t_u, f_u_r):
'''
:type f_t_u: t -> u
:type f_u_r: u -> r
:rtype: t -> r
>>> comp(lambda a: a + 'oppai', lambda b: b + 'hoge')('')
'oppaihoge'
>>> comp(comp(lambda a: a+'oppai', lambda b: b+ 'hoge'), lambda x: '[' + x + ']')('')
'[oppaihoge]'
'''
return lambda t: f_u_r(f_t_u(t))
一応型注釈みたいなものを入れてみたけど読みやすくなっているかどうかは不明.
さて、上記で一応関数合成は出来ているんですが、括弧がいっぱいです。
Scalaよくわかんないんですけども、元記事では括弧をやっつけるために演算子と暗黙の型変換を使っているみたいです。Scalaこわ
がしかし、Pythonはそもそもオレオレ演算子を定義しまくったりはできません。
ということで、ここは関数の中置化で手を打ちます。みんな大好き__ror__
, __or__
のオーバーロードを悪用して、先ほどのcomposeを書き直します。
class infix(object):
def __init__(self, function):
self.function = function
def __ror__(self, other):
self.left = other
return self
def __or__(self, other):
return self.function(self.left, other)
def __call__(self, value1, value2):
return self.function(value1, value2)
@infix
def c(f_t_u, f_u_r): return lambda t: f_u_r(f_t_u(t))
さて、これで
(str.upper |c| sorted |c| set)('abcdeabc')
# > set(['A', 'C', 'B', 'E', 'D'])
のように書くことが出来るようになりました。
最後に、元記事のその2に出てくるApというコンテナを実装しましょう。
関数を受け取って適当するものには、右ビットシフトの演算子を利用します。
class Ap(object):
def __init__(self, val):
self.val=val
def __rshift__(self, func):
return func(self.val)
こいつを使うことで
Ap('abcdeabc') >> (str.upper |c| sorted |c| set)
# > set(['A', 'C', 'B', 'E', 'D'])
と書けるようになりました。成し遂げたぜ。
まだ括弧残ってますけど、もうゴールしてもいいよね。
面白いのでこういうの好きですけども、あんまPython的じゃない気がするので実用性はよくわかんないですね。
# 追記
Apクラスの実装を
class _Ap(object):
def __rlshift__(self, other):
self.value = other
return self
def __rshift__(self, other):
return other(self.value)
Ap = _Ap()
a = Ap
としてしまえば
'abcdacb' <<a>> (str.upper |c| sorted)
#> ['A', 'A', 'B', 'B', 'C', 'C', 'D']
こんな感じにかけますね。
この方が元記事に近いビジュアルですし、よりapply感も出ますね(?)
追記:
汎用性は落ちますが、Haskellっぽく.
で関数合成が出来るような雰囲気にしたものも書いてみました。