计算"关闭"给定改变属性的函数的对象的属性

时间:2017-09-12 12:17:46

标签: python oop object functional-programming state

我有一个对象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个功能组合物。)

输出应该是所有可能输出的列表以及那里的所有路径。因此,如果10493obj.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

我怎么能实现这个?正如评论中所建议的那样,这最好用树来完成,但是虽然我尝试了2天但我几乎没有取得任何进展(我是Python /编程的新手)! 我的例子非常简单,我可以直接使用func(obj)而不是func(22),但我需要处理的示例更复杂,我肯定需要使用对象,所以这只是最小的这方面的工作实例。

树可能不是一个完整的n-ary树,因为每个函数应用程序都将包含一个测试,它是否可以应用于obj的当前状态(属性),在某些情况下还可以测试将obj的{​​属性}保持不变。

2 个答案:

答案 0 :(得分:3)

这是一个简单的例子,它试图找出一个数字(goal)是否是前身 在Collatz conjecture中应用规则时的另一个(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.

答案 1 :(得分:1)

听起来很有趣,让我们把它分解为步骤。

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

  2. 评估可能的功能组合。

  3. 找出可能的组合

    执行此操作的一种方法是使用生成器。它们在内存方面非常高效,因此您最终不会创建一堆值并最大化堆。

    那么我们如何获得所有组合。快速搜索Python文档建议使用itertools。所以,让我们这样做。

    from itertools import combinations
    
    def comb(fns, n):
         return combinations(fns, n)
    

    到目前为止,我们有一个生成器可以为我们提供一系列函数的所有组合(无需替换)。每个提供的组合将是一个函数列表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循环稍慢)。我们也有可读性(特别是如果有人不熟悉发电机)。

    免责声明:我在手机上输入此内容。可能存在轻微的错别字等。