Python相当于Mathematica的Sow/Reap

2024-10-05 11:09:17 发布

您现在位置:Python中文网/ 问答频道 /正文

假设在Mathematica中,我定义了以下函数:

f[list_] := Map[Prime[Sow[#]] &, list];

它输出一个素数列表,这样如果输入列表在位置i有n,那么输出列表将在位置i包含第n个素数。例如

^{pr2}$

现在,如果出于某种原因(调试等…),我想检查输入Prime函数的值。由于函数中的Sow命令,我可以

In[3] := Reap[f[{1, 3, 4}]]

Out[3] := {{2, 5, 7}, {{1, 3, 4}}}

有关播种/收获的更多详细信息,请参阅Wolfram Documentation。我的问题是,是否有一种与Mathematica的播种和收获功能等价的自然Python?特别是,有没有一种方法可以在不显式地从要执行的python函数返回额外的内容的情况下完成这种操作,编写一个几乎相同但返回额外内容的python函数,或者使用全局变量?在


Tags: 函数in命令map内容列表定义out
2条回答

不幸的是,据我所知,Python中没有简单或惯用的对等词“sow”和“reap”。但是,您可能可以使用生成器和装饰器的组合来伪造它,如下所示:

def sow(func):
    class wrapper(object):
        def __call__(self, *args, **kwargs):
            output = list(func(*args, **kwargs))
            return output[-1]

        def reap(self, *args, **kwargs):
            output = list(func(*args, **kwargs))
            final = output[-1]
            intermediate = output[0:-1]
            return [final, intermediate]

    return wrapper()

@sow    
def f(seq, mul):
    yield seq
    yield mul
    yield [a * mul for a in seq]

print f([1, 2, 3], 4)         # [4, 8, 12]
print f.reap([1, 2, 3], 4)    # [[4, 8, 12], [[1, 2, 3], 4]]

然而,与Mathematica相比,这种方法有一些局限性。首先,必须重写该函数,使其使用yield而不是return,将其转换为生成器。最后产生的值就是最终的输出。在

它也没有文档描述的“异常”类属性。decorator @sow只是返回一个类,该类看上去像一个函数,并添加了一个额外的参数reap。在


另一种解决方案可能是尝试使用macropy。因为它直接操作Python的AST和字节码,所以您可能能够将对更符合您所寻找的内容的直接支持组合在一起。tracing宏在意图上与您想要的类似。在

我想出了两种方法来实现这种东西的基本版本,每种方法都有自己的局限性。以下是第一版:

farm = []

def sower(func):
    def wrapped(*args, **kw):
        farm.append([])
        return func(*args, **kw)
    return wrapped

def sow(val):
    farm[-1].append(val)
    return val

def reap(val):
    return val, farm.pop()

您可以这样使用它(基于Mathematica doc页面中的一个示例):

^{pr2}$

这有许多限制:

  1. 任何要使用sow的函数都必须用sower修饰符修饰。这意味着您不能像Mathematica示例那样在内联表达式(如列表理解)中使用sow。您可以通过检查调用堆栈来破解这个问题,但它可能会变得很难看。在
  2. 任何被播种但没有收获的价值都会永远储存在“农场”里,因此农场会随着时间的推移变得越来越大。在
  3. 它没有文档中显示的“标记”功能,尽管这并不难添加。在

写这篇文章让我想到了一个更简单的实现,它有着稍微不同的权衡:

farm = []

def sow(val):
    if farm:
        farm[-1].append(val)
    return val

def reap(expr):
    farm.append([])
    val = expr()
    return val, farm.pop()

您可以这样使用这个,它有点类似于Mathematica版本:

>>> reap(lambda: sum(sow(x**2 + 1) if (x**2 + 1) % 2 == 0 else x**2 + 1 for x in xrange(1, 11)))
(395, [2, 10, 26, 50, 82])

这个函数不需要decorator,它会清除获取的值,但是它使用一个无参数函数作为参数,这需要您将播种表达式包装在一个函数中(这里用lambda完成)。另外,这意味着,被获取的表达式调用的任何函数中的所有播种值都将被插入到同一个列表中,这可能会导致奇怪的排序;我无法从Mathematica文档中判断Mathematica是这样做的还是怎么做的。在

相关问题 更多 >

    热门问题