Python相当于Mathematica的Sow/Reap

JOw*_*wen 10 python wolfram-mathematica

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

f[list_] := Map[Prime[Sow[#]] &, list];
Run Code Online (Sandbox Code Playgroud)

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

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

Out[2]= {2, 5, 7}
Run Code Online (Sandbox Code Playgroud)

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

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

Out[3] := {{2, 5, 7}, {{1, 3, 4}}}
Run Code Online (Sandbox Code Playgroud)

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

Bre*_*arn 5

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

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()
Run Code Online (Sandbox Code Playgroud)

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

>>> @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])
Run Code Online (Sandbox Code Playgroud)

这有许多限制:

  1. 任何想要使用的功能sow都必须由装饰sower器装饰。这意味着您不能sow像Mathematica示例那样使用内部内联表达式(如列表推导)。您可能可以通过检查调用堆栈来破解此文件,但是它可能很难看。
  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()
Run Code Online (Sandbox Code Playgroud)

您可以像这样使用它,它与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])
Run Code Online (Sandbox Code Playgroud)

这个不需要装饰器,它清理了收割的值,但是它采用了无参数函数作为其参数,这要求您将播种表达式包装在一个函数中(在此完成lambda)。同样,这意味着由reaped表达式调用的任何函数中的所有播种值都将插入到同一列表中,这可能导致奇怪的排序;我无法从Mathematica文档中得知这是Mathematica的功能还是什么。