programming教學 - python reduce




計算給定函數改變屬性的對象屬性的“閉包” (2)

下面是一個簡單的例子,當應用Collat​​z猜想中的規則時,試圖找出數字( goal )是否是另一個( inital_state )的前身。

在你的例子中, objstate[func1, func2, ...]是我的例子中的functions 。 此版本將返回最終輸出的路徑,從而最大限度地減少函數應用程序的數量。 您可以通過在循環結束後刪除目標測試並返回prev_states來列出所有狀態,而不是搜索。

from collections import deque

def multiply_by_two(x):
    return x * 2

def sub_one_div_three(x):
    if (x - 1) % 3 == 0:
        return (x - 1) // 3
    else:
        return None # invalid


functions = [multiply_by_two, sub_one_div_three]

# find the path to a given function
def bfs(initial_state, goal):
    initial_path = []
    states = deque([(initial_state, initial_path)])     # deque of 2-tuples: (state, list of functions to get there)
    prev_states = {initial_state}                       # keep track of previously visited states to avoid infinite loop

    while states:
        # print(list(map(lambda x: x[0], states)))      # print the states, not the paths. useful to see what's going on
        state, path = states.popleft()

        for func in functions:
            new_state = func(state)

            if new_state == goal:                       # goal test: if we found the state, we're done
                return new_state, path + [func]

            if (new_state is not None and           # check that state is valid
                new_state not in prev_states):      # and that state hasn't been visited already
                states.append((new_state, path + [func]))
            prev_states.add(new_state)              # make sure state won't be added again
    else:
        raise Exception("Could not get to state")

print(functions)
print(bfs(1, 5))

# prints (5, [<function multiply_by_two at 0x000002E746727F28>, <function multiply_by_two at 0x000002E746727F28>, <function multiply_by_two at 0x000002E746727F28>, <function multiply_by_two at 0x000002E746727F28>, <function sub_one_div_three at 0x000002E7493C9400>]). You can extract the path from here.

我有一個對象obj和一些函數

    def func1(obj):
    #...

    def func2(obj):
    #...

    def func3(obj):
    #...

每一個都改變了obj的屬性的值。

我想我的輸入是類似的東西

obj = MyObject()
obj.attr=22

這應該被傳遞給函數closure() ,該函數計算上述函數的所有可能的應用,意思是func1(func2(obj))func3(func1(func1(obj)))等,直到某個停止條件超過20個功能組成)。

輸出應該是所有可能的輸出以及所有通往那裡的路徑的列表。 所以如果說10493是一個可能的最終產出,如果obj.attr=22 ,並且有兩種方法到達104和一個到達93 。 然後

print closure(obj)

應該是這樣的

[22, 64, 21, 104] #first path to 104 through , func1(obj),func1(func1(obj)), func1(func1(func3(obj)))

[22, 73, 104] #second path to 104 through , func3(obj),func3(func2(obj)), 

[22, 11, 93] #the only path to arrive at 94

我怎麼能實現這個? 正如在評論中提到的,這最好用樹來完成,但是儘管我嘗試了兩天,但是我幾乎沒有取得任何進展(我是Python /編程新手)!
我的例子非常簡單,而不是func(obj)我們可以直接使用func(22)但是我需要處理的例子更加複雜,我肯定需要使用對象,所以這只是一個最小的工作例子為了那個原因。

樹可能不是一個完整的n元樹,因為每個函數應用程序將包含一個測試,它是否可以應用到obj的當前狀態(屬性),在某些情況下測試將失敗(屬性) obj不變。


聽起來很有趣,讓我們把它分解成幾個步驟。

  1. 找出可能的功能組合。

  2. 評估可能的功能組合。

找出可能的組合

一種方法是使用生成器。 它們在內存方面效率很高,所以最終不會創建一堆值並最終堆起來。

那麼我們如何獲得所有的組合。 Python文檔的快速搜索建議使用itertools。 所以讓我們來做。

from itertools import combinations

def comb(fns, n):
     return combinations(fns, n)

到目前為止,我們有一個發生器,可以給我們所有的功能列表(無需替換)的組合。 每個提供的組合都是一個大的函數列表。 我們可以簡單地調用每一個,我們可以得到合成的結果。

這是樹中的一個級別。 我們如何獲得下一個級別。 那麼,我們可以得到大小為1的所有組合,然後是大小為2的所有組合等等。 由於發電機組是懶惰的,我們可以做到這一點,而不會炸毀解釋器。

def combo_tot(fns):
    start=1
    while (start <= len(fns)):
        for c in comb(fns, start):
            yield c
        start += 1

評估可能的組合

所以現在我們有所有可能的組合。 我們用這個來評估一些東西。

def evalf(fns_to_compose, initval):
    v = initval
    for fn in fns_to_compose:
        v = fn(v)
    return v

那就是第二部分。 現在你需要做的就是鏈接。

def results(fns, init):
    return (evalf(fn, init) for fn in combo_tot(fns))

現在只需要盡可能多的結果。

下行

與任何不克隆obj方法一樣。 這將改變對象。 而且,我們有發生器的開銷(可能比for循環稍慢)。 我們也有可讀性的打擊(特別是如果有人不熟悉發電機)。

免責聲明:我正在打電話。 小的錯別字等可能存在。





state