2017-11-17 5 views
1

(放棄:ないPythonの子供を、とても優しくしてください)開梱機能組成、タプルと

を、私は、以下のものを使用しての機能を構成しようとしています:

のための期待どおりに動作
def compose(*functions): 
    return functools.reduce(lambda acc, f: lambda x: acc(f(x)), functions, lambda x: x) 

スカラー関数。私はタプルを返す関数や、複数の引数を取る関数などを使って作業したいと思います。

def dummy(name): 
    return (name, len(name), name.upper()) 

def transform(name, size, upper): 
    return (upper, -size, name) 

# What I want to achieve using composition, 
# ie. f = compose(transform, dummy) 
transform(*dummy('Australia')) 
=> ('AUSTRALIA', -9, 'Australia') 

dummyはタプルを返し、transformは3つの引数を取りますので、私は値を解凍する必要があります。

上記のcompose機能を使用してこれを行うにはどうすればよいですか?私はこのようにしようとすると、私が手:

f = compose(transform, dummy) 
f('Australia') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 2, in <lambda> 
    File "<stdin>", line 2, in <lambda> 
TypeError: transform() takes exactly 3 arguments (1 given) 

必要なところ、それは解凍しますcomposeように変更する方法はありますか?

答えて

1

この1つはあなたの例ために動作しますが、それは文句を言わないだけで、任意の関数を扱う - それは唯一の位置引数で動作しますし、(もちろん)任意の関数のシグネチャは前回(WRT /アプリケーションの戻り値と一致する必要がありますオーダー)1。

def compose(*functions): 
    return functools.reduce(
     lambda f, g: lambda *args: f(*g(*args)), 
     functions, 
     lambda *args: args 
     ) 

注意、むしろunpythonic関数型プログラミングでは、確かに慣用されている間、ここにreduceを使用しています。 「明白な」神託の実装では、反復の代わりに使用します。

def itercompose(*functions): 
    def composed(*args): 
     for func in reversed(functions): 
      args = func(*args) 
     return args  
    return composed 

編集:

あなたは「両方のケースで動作するコン機能を持たせる方法があり、」尋ねる - 「どちらの場合も」ここでは、関数が反復可能かどうかを返すことを意味します(あなたが "スカラー関数"と呼ぶ、Pythonでは意味を持たない概念)。実際にこれはさえある - 期待どおりに動作する

import collections 

def itercompose(*functions): 
    def composed(*args):    
     for func in reversed(functions): 
      if not isinstance(args, collections.Iterable): 
       args = (args,) 
      args = func(*args) 
     return args  
    return composed 

が、これはgaranteedされていません。

、あなただけの戻り値が反復可能であるかどうかをテストし、タプルIEでそれをラップすることができ、反復ベースの実装を使用しますほとんどのユースケースで期待どおりに動作しないことを保証します。 Pythonには多くの組み込み型iterable型があります(さらに多くのユーザ定義型もあります)。オブジェクトを知ることはiterableであるとは言えません。

たとえば、dictまたはstrは反復可能ですが、この場合は明らかに「スカラー」と見なすべきです。 A listもiterableです。この場合、どのように解釈されるべきかは、実際には何が含まれているのか、また合成順序の "次の"関数が何を期待しているのか分からないと決して確定できません。他のケースでは、argsのリストを参照してください。

関数の呼び出し元は、実際に各関数の結果をどのように考慮する必要があるのか​​を実際に知ることができます。実際には、tupleを次の関数によって「スカラー」値とみなしたい場合もあります。長い話をするのは簡単です:いいえ、Pythonで汎用的な解決策はありません。私が考えることができる最高ののは、結果の検査と合成された関数の手作業によるラッピングが必要なので、結果は "合成された"関数によって正しく解釈されますが、この時点で関数を手動で合成することはどちらも簡単で、

FWIWは、Pythonが最初で、主に動的に型付けされたオブジェクト指向言語であることを覚えているので、関数型プログラミングイディオムを適切にサポートしていますが、実際の関数型プログラミングにとっては最良のツールではありません。

+0

あなたが例えば_it'll仕事に少し工夫するだけ任意function_を処理しません気にしませんか?どのようにあなたは機能構成を持っていますか?数学的な観点からは、関数の構成はちょうどそのものです:伝統的に_(g o f)(x):= g(f(x))_で表されるマップの構成。したがって、_g_の署名は、_f_のコード領域内の任意の要素でなければなりません。これはHaskellのような型付き言語ではっきりと分かります。 –

+0

私が言ったことは、キーワードargsで動作しないこと、そして、各関数のシグネチャは、前の関数の戻り値と互換性がなければならないことです。私は実際にはかなり明白な何かを言っていたと思う;) –

+0

もちろん、私は_positional_対_keywords_ argsでビットを欠場した。しかし、あなたの命題はスカラー関数では機能しません。 'compose(operator.abs、operator.neg)' _TypeError:abs()の後の*は、int_でなくシーケンスでなければなりません。どちらの場合でも動作する 'compose'関数を作る方法はありますか? (それは本当に私の元の質問の感覚だった)。 –

0

Brunoから寄せられた答えのcompose関数は、複数の引数を持つ関数に対しては仕事をしましたが、残念なことにスカラーに対してはそれ以上は機能しませんでした。予想通り、今だけの作品

import functools 

def compose(*functions): 
    def pack(x): return x if type(x) is tuple else (x,) 

    return functools.reduce(
    lambda acc, f: lambda *y: f(*pack(acc(*pack(y)))), reversed(functions), lambda *x: x) 

、例えば:Pythonの `位置引数にアンパックタプルが、これは私がそれを解決する方法であるという事実を使用して

######################### 
# scalar-valued functions 
######################### 

def a(x): return x + 1 
def b(x): return -x 

# explicit 
> a(b(b(a(15)))) 
# => 17 

# compose 
> compose(a, b, b, a)(15) 
=> 17 


######################## 
# tuple-valued functions 
######################## 

def dummy(x): 
    return (x.upper(), len(x), x) 
def trans(a, b, c): 
    return (b, c, a) 

# explicit 
> trans(*dummy('Australia')) 
# => ('AUSTRALIA', 9, 'Australia') 

# compose 
> compose(trans, dummy)('Australia') 
# => ('AUSTRALIA', 9, 'Australia') 

そして、これも複数の引数で動作します:

def add(x, y): return x + y 

# explicit 
> b(a(add(5, 3))) 
=> -9 

# compose 
> compose(b, a, add)(5, 3) 
=> -9 
関連する問題