22
18

More than 5 years have passed since last update.

Pythonで関数合成と適用的な

Last updated at Posted at 2013-07-18

関数合成のススメ 〜 オブジェクト指向プログラマへ捧げる関数型言語への導入その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っぽく.で関数合成が出来るような雰囲気にしたものも書いてみました。

Haskellの合成演算子 . をPythonで 〜関数合成によるCollection処理の実装

22
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
18