Wolfram Mathematica 中的 Sum[] 和 Sequence[] [英] Sum[] and Sequence[] in Wolfram Mathematica
问题描述
我需要计算一组可变数量的笛卡尔积的总和.假设 f[...] 是一个多元函数,定义
p[A__set] := Module[{Alist, args, iterators,it},阿里斯特 = {A};我 = 1;迭代器 = {it[i++], Level[#1, 1]} &/@ 一个列表;args = Table[it[i], {i, Range[Length[Alist]]}];Sum[f@@ args, 序列@@ 迭代器]]
然后
p[set[1, 2, 3], set[11, 12, 13]]
给出错误:Sum::vloc: "变量 Sequence@@iterators 不能被本地化,因此它可以被分配给数值."
以下 hack 有效:
p[A__set] := Module[{Alist, args, iterators,it,TmpSymbol},阿里斯特 = {A};我 = 1;迭代器 = {it[i++], Level[#1, 1]} &/@ 一个列表;args = Table[it[i], {i, Range[Length[Alist]]}];Sum@@TmpSymbol[f @@ args, Sequence @@ 迭代器]]
然后
p[set[1, 2, 3], set[11, 12]]
给我想要的:
f[1, 11] + f[1, 12] + f[2, 11] + f[2, 12] + f[3, 11] + f[3, 12]
我想知道为什么原版没有.
根据 belisarius 有更优雅的方法来做到这一点:
p[A__set] := Total[Outer[f, A],Length[{A}]];
这与求值顺序有关.请参阅教程:评估作为参考.
Sum
具有属性 HoldAll
:
属性[总和]
<块引用>
{HoldAll, Protected, ReadProtected}
因此,只有具有某些头部的参数,例如 Evaluate
或 Sequence
或带有 upvalues 的 Symbols 才会计算.您可能认为您的参数 Sequence @@ iterators
具有头部 Sequence
,但它实际上具有头部 Apply
:
HoldForm @ FullForm[序列@@迭代器]
<块引用>
Apply[序列,迭代器]
Sum
需要与其声明的语法相匹配的文字参数,因此您的代码会失败.您可以通过几种不同的方式强制评估.可以说最透明的是添加Evaluate
:
iterators = {{a, 1, 3}, {b, 5, 7}};Sum[a^2/b, Evaluate[序列@@迭代器]]
<块引用>
107/15
更简洁地,您可以利用Function
、SlotSequence
和Apply
;由于 Apply
和 Function
在默认情况下都没有 HoldAll
:
Sum[a^2/b, ##] &@@迭代器
<块引用>
107/15
然而,这两者都有一个潜在的问题:如果 a
或 b
收到一个全局值,iterators
定义中的 Symbol 将评估此值导致另一个错误:
a = 0;Sum[a^2/b, ##] &@@迭代器
<块引用>
Sum::itraw:原始对象 0 不能用作迭代器.>>
相反,您可以将迭代器列表存储在 Hold
表达式中并使用注入器模式" 插入这些值而不进行完整的评估:
iterators = Hold[{a, 1, 3}, {b, 5, 7}];迭代器/._[x__] :>总和[a^2/b, x]
<块引用>
107/15
或者,您可以将 iterators
定义为 upvalue:
Sum[args___, iterators] ^:= Sum[args, {a, 1, 3}, {b, 5, 7}]
现在简单:
Sum[a^2/b, iterators]
<块引用>
107/15
请参阅我对 保持函数范围作为变量的回答 Mathematica.SE 获取更多示例,因为这个问题密切相关.具体参见我的第二个答案中的 setSpec
,它会自动创建 upvalue.
I need to evaluate a sum over Cartesian product of variable number of sets. Assuming f[...] is a multivariate function, define
p[A__set] := Module[{Alist, args, iterators,it},
Alist = {A};
i = 1;
iterators = {it[i++], Level[#1, 1]} & /@ Alist;
args = Table[it[i], {i, Range[Length[Alist]]}];
Sum[f@@ args, Sequence @@ iterators ]
]
But then
p[set[1, 2, 3], set[11, 12, 13]]
Gives the error:
Sum::vloc: "The variable Sequence@@iterators cannot be localized so that it can be assigned to numerical values."
The following hack works:
p[A__set] := Module[{Alist, args, iterators,it,TmpSymbol},
Alist = {A};
i = 1;
iterators = {it[i++], Level[#1, 1]} & /@ Alist;
args = Table[it[i], {i, Range[Length[Alist]]}];
Sum@@TmpSymbol[f @@ args, Sequence @@ iterators ]
]
Then
p[set[1, 2, 3], set[11, 12]]
gives what I want:
f[1, 11] + f[1, 12] + f[2, 11] + f[2, 12] + f[3, 11] + f[3, 12]
I would like to know why the original does not.
As per belisarius there is much more elegant way to do this:
p[A__set] := Total[Outer[f, A],Length[{A}]];
This has to do with evaluation order. Please see Tutorial: Evaluation as a reference.
Sum
has the Attribute HoldAll
:
Attributes[Sum]
{HoldAll, Protected, ReadProtected}
Because of this only arguments with certain heads such as Evaluate
or Sequence
or Symbols with upvalues will evaluate. You may think that your argument Sequence @@ iterators
has the head Sequence
, but it actually has the head Apply
:
HoldForm @ FullForm[Sequence @@ iterators]
Apply[Sequence, iterators]
Sum
expects literal arguments that match its declared syntax, and thus your code fails. You can force evaluation in several different ways. Arguably the most transparent is to add Evaluate
:
iterators = {{a, 1, 3}, {b, 5, 7}};
Sum[a^2/b, Evaluate[Sequence @@ iterators]]
107/15
More concisely you can leverage Function
, SlotSequence
, and Apply
; evaluation takes place since neither Apply
, nor Function
by default, has HoldAll
:
Sum[a^2/b, ##] & @@ iterators
107/15
Both of these have a potential problem however: if a
or b
received a global value the Symbol in the definition of iterators
will evaluate to this value causing another error:
a = 0;
Sum[a^2/b, ##] & @@ iterators
Sum::itraw: Raw object 0 cannot be used as an iterator. >>
Instead you can store the iterator lists in a Hold
expression and use the "injector pattern" to insert these values without complete evaluation:
iterators = Hold[{a, 1, 3}, {b, 5, 7}];
iterators /. _[x__] :> Sum[a^2/b, x]
107/15
Alternatively you could define iterators
as an upvalue:
Sum[args___, iterators] ^:= Sum[args, {a, 1, 3}, {b, 5, 7}]
Now simply:
Sum[a^2/b, iterators]
107/15
Please see my answers to Keep function range as a variable on Mathematica.SE for more examples, as this question is closely related. Specifically see setSpec
in my second answer which automates the upvalue creation.
这篇关于Wolfram Mathematica 中的 Sum[] 和 Sequence[]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!