在功能风格上作出的Conjoin功能 [英] Conjoin function made in functional style

查看:64
本文介绍了在功能风格上作出的Conjoin功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近,阅读Python 功能编程HOWTO,我碰到一个提到的那里 test_generators.py 标准模块,其中我找到了以下生成器:

  #conjoin是一个简单的回溯生成器,以Icon的
#连接控制结构命名。传递返回可迭代对象的无参数函数
#的列表。最简单的例子就是:假设传递
#函数列表[x,y,z]。然后结合行为:

#def g():
#values = [None] * 3
#用于x()中的值[0]:$ b $在y()中,b的值为[1]:
#为z()中的值[2]:
#yield值

#可能会生成值*,每次我们成功
#进入最内层循环。如果一个迭代器在
#之前失败(耗尽),那么它会回溯以从最近的包含
#迭代器(一个向左)获取下一个值,并重新开始在下一个
#插槽(泵一个新的迭代器)。当
#迭代器有副作用时,这当然是最有用的,这样每个时隙在
#上可以产生哪些值*取决于在以前的时隙迭代的值。

def simple_conjoin(gs):

values = [None] * len(gs)

def gen(i):
如果i> = len(gs):
产生值
else:
对于gs [i]()中的值[i]:
对于gen(i + 1):
产量x

(x):x
产量x

我花了一段时间才了解它是如何工作的。它使用一个可变列表 values 来存储迭代器的结果,而N + 1迭代器返回 ,它贯穿迭代器的整个链。



当我在阅读函数式编程的时候偶然发现了这段代码,我开始考虑是否可以重写这个联合生成器使用函数式编程(使用 itertools 模块中的函数)。
有很多以函数式编写的例程(只需浏览



但不幸的是,我还没有找到任何解决方案。



那么,是否可以使用函数式编程来编写这个联合生成器,只需使用 itertools module



谢谢

解决

  def conjoin(gs):$ b这个方法似乎有效, $ b返回[()]如果不是gs else(
(val,))+ gs [0]()后缀为suffix in conjoin(gs [1:])


def range3():
返回范围(3)

打印列表(conjoin([range3,range3]))




$ b

输出:

  [(0,0) ,(0,1),(0,2),(1,0),(1,1),(1 ,2),(2,0),(2,1),(2,2)] 

示例显示可变状态的用法:

  x =
def mutablerange():
global x
x + =x
返回[x + str(i)为我在范围内(3)]

打印列表(conjoin([range3,mutablerange]))

输出:(注意'x'的数量增加)
$ b (0,'x1'),(0,'x2'),(1,'xx0'),(1, 'xx1'),(1,'xx2'),(2,'xxx0'),(2,'xxx1'),(2,'xxx2')]
pre>

如果我们使用 itertools.product

  x =
打印列表(itertools.product(range3(),mutablerange()))

$ b

结果如下:

$ $ $ $ $ $ $ $ $ $ $([0,'x0 ''),(1,'x1'),(1,'x2'),(1,'x0'), ,(2,'x1'),(2,'x2')]

早期看到, itertools.product 会缓存迭代器返回的值。


Recently, reading Python "Functional Programming HOWTO", I came across a mentioned there test_generators.py standard module, where I found the following generator:

# conjoin is a simple backtracking generator, named in honor of Icon's
# "conjunction" control structure.  Pass a list of no-argument functions
# that return iterable objects.  Easiest to explain by example:  assume the
# function list [x, y, z] is passed.  Then conjoin acts like:
#
# def g():
#     values = [None] * 3
#     for values[0] in x():
#         for values[1] in y():
#             for values[2] in z():
#                 yield values
#
# So some 3-lists of values *may* be generated, each time we successfully
# get into the innermost loop.  If an iterator fails (is exhausted) before
# then, it "backtracks" to get the next value from the nearest enclosing
# iterator (the one "to the left"), and starts all over again at the next
# slot (pumps a fresh iterator).  Of course this is most useful when the
# iterators have side-effects, so that which values *can* be generated at
# each slot depend on the values iterated at previous slots.

def simple_conjoin(gs):

    values = [None] * len(gs)

    def gen(i):
        if i >= len(gs):
            yield values
        else:
            for values[i] in gs[i]():
                for x in gen(i+1):
                    yield x

    for x in gen(0):
        yield x

It took me a while to understand how it works. It uses a mutable list values to store the yielded results of the iterators, and the N+1 iterator return the values, which passes through the whole chain of the iterators.

As I stumbled into this code while reading about functional programming, I started thinking if it was possible to rewrite this conjoin generator using functional programming (using functions from the itertools module). There are a lot of routines written in functional style (just glance at the end of this article in the Recipes section).

But, unfortunately, I haven't found any solution.

So, is it possible to write this conjoin generator using functional programming just using the itertools module?

Thanks

解决方案

This seems to work, and it's still lazy:

def conjoin(gs):
    return [()] if not gs else (
        (val,) + suffix for val in gs[0]() for suffix in conjoin(gs[1:])
    )

def range3():
    return range(3)

print list(conjoin([range3, range3]))

Output:

[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

Example usage to show mutable state:

x = ""
def mutablerange():
    global x
    x += "x"
    return [x + str(i) for i in range(3)]

print list(conjoin([range3, mutablerange]))

Output: (watch the increasing number of 'x's)

[(0, 'x0'), (0, 'x1'), (0, 'x2'), (1, 'xx0'), (1, 'xx1'), (1, 'xx2'), (2, 'xxx0'), (2, 'xxx1'), (2, 'xxx2')]

And if we use itertools.product:

x = ""
print list(itertools.product(range3(), mutablerange()))

the result is the following:

[(0, 'x0'), (0, 'x1'), (0, 'x2'), (1, 'x0'), (1, 'x1'), (1, 'x2'), (2, 'x0'), (2, 'x1'), (2, 'x2')]

So, one clearly see, that itertools.product caches the values returned by the iterator.

这篇关于在功能风格上作出的Conjoin功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆