2017-08-04 52 views
3

我想了解reduce如何通過此網站工作。他們提到的例子很好,很容易理解。lambda如何與減少

http://book.pythontips.com/en/latest/map_filter.html#reduce

a = reduce((lambda x, y: x * y), [1, 2, 3, 4]) 

上述功能將在列表中的多個的每一個號碼,並分配給a

但是,當我在項目中遇到以下功能時,我完全陷入了困境。

def compose(*fns): 
    return reduce(lambda acc, fn: lambda *args: acc(fn(*args)), fns, lambda _: _) 

可能有人幫助我擊穿此功能來了解它的假設做

回答

1

lambda表達式可能難以跟隨,並且尤其如此與這一個,它返回一個新的功能,採用lambda還限定。

下面是同樣的表情,但也有一些不同的線間距:

def compose_2_fns(f, g): 
    # take 2 functions and return a new function that calls the first with the 
    # result of calling the second 
    def composed(*args): 
     return f(g(*args)) 
    return composed 

def _initial(x): 
    return x 

def compose(*fns): 
    return reduce(compose_2_fns, fns, _initial) 

def compose(*fns): 
    return reduce(lambda acc, fn: lambda *args: acc(fn(*args)), 
        fns, 
        lambda _: _) 

現在,我將進一步爆炸的lambda秒後,以reduce作爲常規def陳述擴大這個

回想一下,reduce的工作原理是給它一個帶有2個參數,一系列對象(在本例中是一系列函數)和一個可選的初始值的方法。

reduce(reduce_fn, objs, first_obj) 

如果沒有初始值給出,減少將採取的第一個對象序列中,就好像你稱它像是:

reduce(reduce_fn, objs[1:], objs[0]) 

然後減少函數被調用是這樣的:

accumulator = first_obj 
for obj in objs: 
    accumulator = reduce_fn(accumulator, obj) 
return accumulator 

因此,您發佈的reduce聲明正在通過組合幾個較小的聲明來建立一個大功能。

functions = (add_1, mult_5, add_3) 
resulting_function -> lambda *args: add_1(mult_5(add_3(*args))) 

這樣:

resulting_function(2) -> (((2 + 3) * 5) + 1) -> 26 
+0

這真是一個令人困惑的函數。感謝您使它變得非常簡單。 –

2

reduce僅僅是一個函數的作曲家。它接受可迭代的函數並將它們組合起來。

有關reduce很重要的一點是,當你通過2個參數的函數(在這種情況下accfn)的基礎上,你如何使用它們,在第一次迭代Python使用在迭代的最後兩個參數,而不是他們,並在接下來的迭代中,它使用最新計算的結果而不是第二個參數傳遞給lamda或作爲構造函數傳遞給reduce(本例中爲fn)的任何函數。現在,爲了更好地展示這裏是區分傳遞給reduce的所有參數:

功能:lambda acc, fn: lambda *args: acc(fn(*args))

  • 使用第二個功能是能夠在每次迭代的參數傳遞給內部函數。

迭代參數:fns

初始參數:lambda _: _

如果初始存在,則它被放置在計算序列的項 之前,並用作默認當 序列爲空。

正如你可以看到通過它的功能與參數調用內部的功能,並把它傳遞給數學一樣fog下一個功能是f(g(x))。但既然你可以傳遞一個未定義數量的參數來減少這個函數可以組成多個函數。

下面是一個例子:

In [10]: compose(max, min)([[2, 4], [3, 5]]) 
Out[10]: 4 

另外請注意,這不是一個Python的,並建議在所有組成功能的方式。因爲首先它不可讀和易於理解,其次它使用了很多額外的函數調用,這對於這樣的任務來說不是最佳的。

2

一見鍾情不太明朗。讓我們來分解:

首先,def compose(*fns):意味着撰寫函數將收到未知數​​量的參數。

接下來,讓我們分解減少功能:

reduce(
    lambda acc, fn: lambda *args: acc(fn(*args)), 
    fns, 
    lambda _: _ 
) 

隨着doc表示,reduce接受3個參數:

def reduce(function, iterable, initializer=None): 

所以,你的情況:在functionlambda acc, fn: lambda *args: acc(fn(*args))fnsiterable,並且它將被初始化爲lambda _: _

initializer表示compose的參數將是函數。 lambda _: _是一個函數的「中性元素」(與「0」相同或相乘爲「1」)。我想這基本上是在fns空了的時候。

現在的主要部分:

lambda acc, fn: lambda *args: acc(fn(*args)) 

這是採取兩種功能accfn並返回lambda函數lambda *args: acc(fn(*args))的功能。

讓我們舉個例子:

>>> reduce((lambda acc, fn: acc ** fn), [1, 2, 3, 4]) 
1 
>>> reduce((lambda acc, fn: fn ** acc), [1, 2, 3, 4]) 
262144 

這裏ACC和FN不是功能,而是整數。 acc是迄今爲止的「累積/減少」,而fn是「下一個」步驟。

有了函數,它將是相同的acc到目前爲止是「調用函數」,而下一個函數是fn。

所以lambda acc, fn: lambda *args: acc(fn(*args))將返回一個(lambda)函數,它將返回acc(fn(the_arguments))

reduce(lambda acc, fn: lambda *args: acc(fn(*args)), fns, lambda _: _)將返回一個函數,該函數包含將fns的每個函數應用於其參數,默認情況下爲標識(lambda _: _)。

讓我們舉個例子:

>>> def square(x): 
... return x**2 
... 
>>> def increment(x): 
... return x+1 
... 
>>> def half(x): 
... return x/2 
... 
>>> compose(square, increment, half) 
<function <lambda> at 0x7f5321e13de8> 
>>> g=compose(square, increment, half) 
>>> g(5) 
9 

所以,g(x) = square(increment(half(x)))


隨着Kasramvd's example

compose(max, min)([[2, 4], [3, 5]]) 

是一樣的:

max(min([[2, 4], [3, 5]])) 

min([[2, 4], [3, 5]])意願返回[2,4]max([2,4])是4。因此compose(max, min)([[2, 4], [3, 5]])=4

+0

我喜歡_「的'initializer'表明compose'的'的論點將是功能'拉姆達_:_'是功能»中性元素« (與'0'相同或乘以'1')。「_ – gboffi