tel*_*f14 7 localization wolfram-mathematica function
使用消息Set
提醒我们,可以轻松地跨两个列表进行多个分配,而不必分开任何内容.例如:
Remove[x1, x2, y1, y2, z1, z2];
{x1, x2} = {a, b}
Run Code Online (Sandbox Code Playgroud)
执行赋值并返回:
{a, b}
Run Code Online (Sandbox Code Playgroud)
Thread
,通常用于生成规则列表,也可以显式调用以实现相同的结果:
Thread[{y1, y2} = {a, b}]
Thread[{z1, z2} -> {a, b}]
Run Code Online (Sandbox Code Playgroud)
得到:
{a, b}
{z1 -> a, z2 -> b}
Run Code Online (Sandbox Code Playgroud)
但是,使用此方法生成局部常量会产生错误.考虑这个简单的示例函数:
Remove[f];
f[x_] :=
With[{{x1, x2} = {a, b}},
x + x1 + x2
]
f[z]
Run Code Online (Sandbox Code Playgroud)
这里出现错误信息:
With::lvset: "Local variable specification {{x1,x2}={a,b}} contains
{x1,x2}={a,b}, which is an assignment to {x1,x2}; only assignments
to symbols are allowed."
Run Code Online (Sandbox Code Playgroud)
错误消息documentation(ref/message/With/lvw
)在"更多信息"部分中说," 当With中的第一个元素不是符号赋值列表时,将生成此消息." 鉴于这种解释,我理解为什么我的任务失败的机制.尽管如此,我很困惑并且想知道这是否是WRI的必要限制,还是应该报告的次要设计监督.
所以这是我的问题:
任何人都可以对这种行为有所了解和/或提供解决方法吗?我试图强迫Evaluation
,没有运气,我不知道还有什么可以尝试.
Leo*_*rin 11
你要求的是棘手的.这是宏的工作,已经被其他人公开了.我将探索一种不同的可能性 - 使用相同的符号,但在您要编写的代码周围放置一些包装器.这种技术的优点是代码在"词法"和"编译时"转换,而不是在运行时转换(如在其他答案中).这通常更快,更容易调试.
所以,这里有一个函数可以改变With
你提出的语法:
Clear[expandWith];
expandWith[heldCode_Hold] :=
Module[{with},
heldCode /. With -> with //. {
HoldPattern[with[{{} = {}, rest___}, body_]] :>
with[{rest}, body],
HoldPattern[
with[{
Set[{var_Symbol, otherVars___Symbol}, {val_, otherVals___}], rest___},
body_]] :>
with[{{otherVars} = {otherVals}, var = val, rest}, body]
} /. with -> With]
Run Code Online (Sandbox Code Playgroud)
请注意,这适用于保留的代码.这样做的好处是,我们不必担心在开始时expandWith
或完成时都不能对代码进行评估.下面是它的工作原理:
In[46]:= expandWith@Hold[With[{{x1,x2,x3}={a,b,c}},x+x1+x2+x3]]
Out[46]= Hold[With[{x3=c,x2=b,x1=a},x+x1+x2+x3]]
Run Code Online (Sandbox Code Playgroud)
然而,这使用起来不太方便.这是一个简化这个的便利功能:
ew = Function[code, ReleaseHold@expandWith@Hold@code, HoldAll]
Run Code Online (Sandbox Code Playgroud)
我们现在可以用它:
In[47]:= ew@With[{{x1,x2}={a,b}},x+x1+x2]
Out[47]= a+b+x
Run Code Online (Sandbox Code Playgroud)
因此,要在代码中进行扩展,只需将其ew
环绕.以下是函数定义的情况:
Remove[f];
ew[f[x_] := With[{{x1, x2} = {a, b}}, x + x1 + x2]]
Run Code Online (Sandbox Code Playgroud)
我们现在检查并看到我们得到的是扩展定义:
?f
Global`f
f[x_]:=With[{x2=b,x1=a},x+x1+x2]
Run Code Online (Sandbox Code Playgroud)
这种方法的优点是可以包装ew
任意大块的代码.首先,从它生成扩展代码,就像你自己编写代码一样,然后执行该代码.对于函数定义的情况,f
如上所述,我们可以认为代码生成发生在"编译时",因此您在以后使用该函数时可以避免任何运行时开销,如果经常调用该函数,这可能很大.
这种方法的另一个优点是它的可组合性:你可以提出许多语法扩展,并为每个语法扩展写一个类似的函数ew
.然后,如果这些自定义代码转换函数不会相互冲突,您可以简单地组合(嵌套)它们,以获得累积效果.从某种意义上说,通过这种方式,您可以创建一个自定义代码生成器,该代码生成器从代表自定义语言中的程序的Mathematica表达式生成有效的Mathematica代码,您可以使用这些方法在Mathematica中创建这些代码.
编辑
在写作中expandWith
,我使用迭代规则应用程序来避免处理评估控制,这可能是一团糟.但是,对于那些感兴趣的人,这里有一个版本,它使用未评估的代码片段进行一些明确的工作.
Clear[expandWithAlt];
expandWithAlt[heldCode_Hold] :=
Module[{myHold},
SetAttributes[myHold, HoldAll];
heldCode //. HoldPattern[With[{Set[{vars__}, {vals__}]}, body_]] :>
With[{eval =
(Thread[Unevaluated[Hold[vars] = Hold[vals]], Hold] /.
Hold[decl___] :> myHold[With[{decl}, body]])},
eval /; True] //. myHold[x_] :> x]
Run Code Online (Sandbox Code Playgroud)
我发现它比第一个复杂得多.
棘手的问题是保持Set未评估的第一个参数.这是我的建议(当然可以改进):
SetAttributes[myWith, HoldAll];
myWith[{s : Set[a_List, b_List]}, body_] :=
ReleaseHold@
Hold[With][
Table[Hold[Set][Extract[Hold[s], {1, 1, i}, Hold],
Extract[Hold[s], {1, 2, i}]], {i, Length@b}], Hold@body]
x1 = 12;
Remove[f];
f[x_] := myWith[{{x1, x2} = {a, b}}, x + x1 + x2]
f[z]
Run Code Online (Sandbox Code Playgroud)
结果是
a+b+z
Run Code Online (Sandbox Code Playgroud)
受到下面的halirutan的启发,我认为他的解决方案稍微更加安全,与上述相同:
SetAttributes[myWith, HoldAll];
myWith[{Set[a : {__Symbol}, b_List]} /; Length[a] == Length[b],
body_] :=
ReleaseHold@
Hold[With][
Replace[Thread[Hold[a, b]], Hold[x_, y_] :> Hold[Set[x, y]], 1],
Hold@body]
Run Code Online (Sandbox Code Playgroud)
"LocalConstants"教程说
使用[{x =下标[x,0],...},body]的方法是获取body,并用Subscript [x,0]等替换它中的每一个x等等.你可以把它想象成/的概括.运算符,适用于Mathematica代码而不是其他表达式.
参考这个解释似乎很明显
x + x1 + x2 /. {x1, x2} -> {a, b}
Run Code Online (Sandbox Code Playgroud)
不会像在With符号中那样工作.
让我们假设你真的想破解这个.With[]
具有HoldAll属性,因此不会评估您作为第一个参数提供的所有内容.要进行这样的矢量赋值工作,您必须创建
With[{x1=a, x2=b}, ...]
Run Code Online (Sandbox Code Playgroud)
来自矢量符号.不幸,
Thread[{a, b} = {1, 2}]
Run Code Online (Sandbox Code Playgroud)
不起作用,因为没有保持Thread的参数,并且在Thread可以执行任何操作之前评估赋值.
让我们解决这个问题
SetAttributes[myThread, HoldFirst];
myThread[Set[a_, b_]] := mySet @@@ Transpose[{a, b}]
Run Code Online (Sandbox Code Playgroud)
给
In[31]:= myThread[{a, b, c} = {1, 2, 3}]
Out[31]= {mySet[a, 1], mySet[b, 2], mySet[c, 3]}
Run Code Online (Sandbox Code Playgroud)
一开始看起来很有希望,只是把问题转移了一下.要使用此功能,With[]
您必须在某些时候将mySet替换为真实的Set.恰好在那时,With[]
没有看到列表{a = 1,b = 2,c = 3}但是,因为必须对其进行评估,所有作业的结果
In[32]:= With[
Evaluate[myThread[{a, b, c} = {1, 2, 3}] /. mySet :> Set], a + b + c]
During evaluation of In[32]:= With::lvw: Local
variable specification {1,2,3} contains 1, which is not an assignment to a symbol. >>
Out[32]= With[{1, 2, 3}, a + b + c]
Run Code Online (Sandbox Code Playgroud)
似乎并不容易解决这个问题,这里还有第二个问题:如果有一种方法可以绕过这种限制,它是否会像With那样快或者与模块相比失去速度优势?如果速度不是那么重要,为什么不首先使用Module或Block?