Wolfram Mathematica 中的 Sum[] 和 Sequence[]

use*_*572 3 wolfram-mathematica

我需要评估可变数量集合的笛卡尔积的总和。假设 f[...] 是一个多元函数,定义

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

但是之后

p[set[1, 2, 3], set[11, 12, 13]]
Run Code Online (Sandbox Code Playgroud)

给出错误: Sum::vloc: "The variable Sequence@@iterators cannot be localized so that it can be assigned to numerical values."

以下黑客工作:

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

然后

p[set[1, 2, 3], set[11, 12]]
Run Code Online (Sandbox Code Playgroud)

给我想要的:

f[1, 11] + f[1, 12] + f[2, 11] + f[2, 12] + f[3, 11] + f[3, 12]
Run Code Online (Sandbox Code Playgroud)

我想知道为什么原来没有。

根据belisarius有更优雅的方法来做到这一点:

p[A__set] := Total[Outer[f, A],Length[{A}]];
Run Code Online (Sandbox Code Playgroud)

Mr.*_*ard 5

这与评估顺序有关。请参阅教程:评估作为参考。

Sum有属性HoldAll

Attributes[Sum]
Run Code Online (Sandbox Code Playgroud)
{HoldAll, Protected, ReadProtected}
Run Code Online (Sandbox Code Playgroud)

因此,只有具有某些头部的参数,例如带有upvalues 的EvaluateSequence或 Symbols才会评估。你可能认为你的论点有 head ,但它实际上有 head :Sequence @@ iteratorsSequenceApply

HoldForm @ FullForm[Sequence @@ iterators]
Run Code Online (Sandbox Code Playgroud)
Apply[Sequence, iterators]
Run Code Online (Sandbox Code Playgroud)

Sum期望与其声明的语法匹配的文字参数,因此您的代码失败。您可以通过几种不同的方式强制评估。可以说最透明的是添加Evaluate

iterators = {{a, 1, 3}, {b, 5, 7}};

Sum[a^2/b, Evaluate[Sequence @@ iterators]]
Run Code Online (Sandbox Code Playgroud)
107/15
Run Code Online (Sandbox Code Playgroud)

更简洁,你可以利用FunctionSlotSequenceApply; 评估发生,因为无论是Apply还是Function默认情况下,都没有HoldAll

Sum[a^2/b, ##] & @@ iterators
Run Code Online (Sandbox Code Playgroud)
107/15
Run Code Online (Sandbox Code Playgroud)

然而,这两者都有一个潜在的问题:如果ab收到一个全局值,定义中的 Symboliterators将评估为该值,导致另一个错误:

a = 0;

Sum[a^2/b, ##] & @@ iterators
Run Code Online (Sandbox Code Playgroud)

Sum::itraw:原始对象 0 不能用作迭代器。>>

相反,您可以将迭代器列表存储在一个Hold表达式中,并使用“注入器模式”来插入这些值而不进行完整的评估:

iterators = Hold[{a, 1, 3}, {b, 5, 7}];

iterators /. _[x__] :> Sum[a^2/b, x]
Run Code Online (Sandbox Code Playgroud)
107/15
Run Code Online (Sandbox Code Playgroud)

或者,您可以定义iteratorsupvalue

Sum[args___, iterators] ^:= Sum[args, {a, 1, 3}, {b, 5, 7}]
Run Code Online (Sandbox Code Playgroud)

现在简单地:

Sum[a^2/b, iterators]
Run Code Online (Sandbox Code Playgroud)
107/15
Run Code Online (Sandbox Code Playgroud)

有关更多示例,请参阅我在Mathematica .SE上将函数范围作为变量的答案,因为这个问题密切相关。具体参见我的第二个答案,它可以自动创建upvaluesetSpec