2017-11-17 3 views
1

풀고 (부인 :하지 파이썬 아이, 그래서 부드러운 주시기 바랍니다가)기능 구성, 튜플 및

나는 다음과 같은 사용 기능을 구성하려고 :

에 대한 예상대로 작동
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 세 가지 인수를, 나는 값을 압축 해제해야합니다.

위의 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

예를 들어에 대해 작동하지만 임의의 함수는 처리하지 않습니다. 이것은 위치 인수와 함께 작동하며 (물론) 모든 함수의 서명은 이전의 반환 값과 일치해야합니다 (wrt/응용 프로그램). 주문). 여기 reduce를 사용하여 함수형 프로그래밍에서 확실히 관용적하면서, 오히려 unpythonic 것을

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

참고. 은 "명백한"파이썬 구현은 반복 대신 사용합니다 :

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되지 않습니다

, 당신은 단지 반환 값이 반복 가능한 경우 테스트하고 튜플 즉 그것을 포장 할 수있는 반복 기반 구현을 사용하여 대부분의 사용 사례에서 예상대로 작동하지 않습니다. 파이썬에는 많은 내장 iterable 타입이 있고 (심지어 더 많은 사용자 정의 타입이 있습니다) 객체를 알고 있다는 것은 iterable은 의미 론적으로 많이 쓰지 않는다는 것을 알고 있습니다.

예를 들어 dict 또는 str은 반복 가능하지만이 경우 분명히 "스칼라"로 간주되어야합니다. A list도 iterable이며,이 경우 해석되어야하는 방식은 정확히 무엇이 포함되어 있고 무엇이 포함되는지 정확히 알지 못하고 결정할 수있는 순서대로 "다음"기능이 무엇인지 예상하지 못합니다. 어떤 경우에는 단일 인수로 취급해야합니다. , 다른 경우에는 args 목록을 ase.

함수의 호출자 만이 각 함수 결과를 고려해야 할 방법을 알 수 있습니다. 실제로는 다음 함수에서 tuple을 "스칼라"값으로 간주하려는 경우도 있습니다. 긴 이야기를 짧게하기 위해 : 아니오, 파이썬에서 일반적인 해결책은 없습니다.내가 생각할 수있는 최선의 방법은 결과 검사와 작성된 함수의 수동 배치가 필요하므로 결과가 "작성된"함수에 의해 적절하게 해석되지만,이 시점에서 함수를 수동으로 작성하는 것은 더 간단하고 훨씬 강력합니다.

FWIW는 파이썬이 가장 먼저 동적으로 유형화 된 객체 지향 언어이므로 함수 프로그래밍 관용구를 제대로 지원하지만 분명히 실제 함수 프로그래밍을위한 최상의 도구는 아닙니다.

+0

당신이 당신의 예를 들어 _it'll 작업에 조금 정교하게 될까요 그냥 임의의 FUNCTION_을 처리하지 않습니다? 어떻게 다른 기능 구성을 가지고 있습니까? 수학적 관점에서 볼 때, 함수 구성은 바로 다음과 같습니다. 전통적으로 _ (g o f) (x) : = g (f (x)) _로 표시된지도의 구성. 그러므로, _g_의 서명은 _f_의 코 도로 마내에있는 요소이어야한다. 이것은 Haskell과 같은 형식화 된 언어에서 분명합니다. –

+0

내가 말한 것은 키워드 args와 함께 작동하지 않으며 각 함수의 서명이 이전 값의 반환 값과 호환되어야한다는 것입니다. 나는 실제로 꽤 명백한 무언가를 말하고 있었다고 생각한다;) –

+0

물론, 나는 _positional_ vs. _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) 

, 예를 들면 : 파이썬`위치 인수로 압축이 '튜플이 내가 그것을 해결하는 방법이라는 사실을 사용하여

.

######################### 
# 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