繁体   English   中英

Python相当于Mathematica的Sow / Reap

[英]Python equivalent of Mathematica's Sow/Reap

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

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

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

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

Out[2]= {2, 5, 7}

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

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

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

有关Sow / Reap的更多详细信息,请参阅Wolfram文档 我的问题是, 有没有相当于Mathematica的Sow and Reap功能的天然Python? 特别是,有没有办法做这种事情,而不是从你想要做的python函数显式返回额外的东西,编写几乎相同但返回额外的东西,或使用全局变量的第二个python函数?

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

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文档页面中的一个示例):

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

这有许多限制:

  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])

这个不需要装饰器,它可以清除重新获取的值,但它需要一个无参数函数作为它的参数,这需要你将播种表达式包装在一个函数中(这里用lambda完成)。 此外,这意味着所收集的表达式调用的任何函数中的所有播放值都将插入到同一列表中,这可能导致奇怪的排序; 我无法从Mathematica博士那里了解这是Mathematica的作用或内容。

不幸的是,据我所知,在“播种”和“收获”的Python中没有简单或惯用的等价物。 但是,您可以使用生成器和装饰器的组合伪造它,如下所示:

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,将其转换为生成器。 产生的最后一个值将是最终输出。

它也没有与文档描述的相同的“异常”属性。 装饰者@sow只是返回一个假装看起来像一个函数的类,并添加了额外的参数reap


另一种解决方案可能是尝试使用macropy 由于它直接操作Python的AST和字节码,因此您可能能够直接支持直接支持更符合您所需要的内容。 跟踪宏看起来与您想要的意图相似。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM