生成本地化变量时出错(作为常量) [英] Error generating localized variables (as constants)

查看:128
本文介绍了生成本地化变量时出错(作为常量)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Set 的用法消息提醒我们,可以轻松地在两个列表中进行多个赋值,而不必拆开任何东西。例如:

 删除[x1,x2,y1,y2,z1,z2]; 
{x1,x2} = {a,b}

执行赋值并返回:

  {a,b} 

通常用于生成规则列表的线程也可以显式调用以实现相同的结果:



线程[{z1,z2} - >线程[{y1,y2} = {a,b}]
线程[{z1,z2} {a,b}]

给出:

  {a,b} 
{z1 - > a,z2 - > b}

然而,使用这种方法生成局部常量会产生错误。考虑这个简单的示例函数:

 删除[f]; 
f [x_]:=
用[{{x1,x2} = {a,b}},
x + x1 + x2
]
f [z]

这里的错误信息:

 使用:: lvset:局部变量规范{{x1,x2} = {a,b}}包含
{x1,x2} = {a,b}到{x1,x2};只允许分配
给符号。

错误消息文档( ref / message / With / lvw ),在'更多信息'部分中说:当With中的第一个元素不是符号赋值列表时产生此消息。给出这个解释,我理解为什么我的任务失败的机制。然而,我很困惑,并想知道这是WRI的必要限制,还是应该报告的小型设计监督。

所以这是我的问题:



任何人都可以阐明这种行为并/或提供解决方法吗?我尝试强制评估,没有运气,我不知道还有什么可以尝试的。 你所要求的是棘手的。正如其他人已经暴露的那样,这是宏的工作。我将探索一种不同的可能性 - 使用相同的符号,但在要编写的代码周围放置一些包装。这种技术的优点是代码在词法上和编译时转换,而不是在运行时(如其他答案)。这通常既快又容易调试。



所以,这里有一个函数可以用你提出的语法转换 With >

 清除[expandWith]; 
expandWith [heldCode_Hold]:=
Module [{with},
heldCode /。使用 - >与/ /。 {
HoldPattern [with [{{} = {},rest___},body_]]:>
with [{rest},body],
HoldPattern [
with [{
Set [{var_Symbol,otherVars___Symbol},{val_,otherVals___}],rest___},
body_]]:>
与[{{otherVars} = {otherVals},var = val,rest},正文]
} /。与 - >使用]

请注意,这是对保留的代码进行操作。这具有以下优点:我们不必担心在开始时以及 expandWith 完成时可能对代码进行评估。这是它是如何工作的:

  In [46]:= expandWith @ Hold [With [{{x1,x2,x3} = [x3 = c,x2 = b,x1 = a},x + x1 + x1 = x2 + x3]] 

然而,使用起来并不方便。这里有一个便捷函数来简化它:

  ew =函数[code,ReleaseHold @ expandWith @ Hold @ code,HoldAll] 

我们现在可以使用它:

<$ p $ {p> In [47]:= ew @ With [{{x1,x2} = {a,b}},x + x1 + x2]
Out [47] = a + b + x

所以,为了在代码中进行扩展,只需将 ew 在它周围。这里是你的函数定义的情况:

 删除[f]; 
ew [f [x_]:= With [{{x1,x2} = {a,b}},x + x1 + x2]]

我们现在检查并看到我们得到的是一个扩展的定义:

 ?f 
全局`f
f [x _]:=使用[{x2 = b,x1 = a},x + x1 + x2]

这种方法的优点是可以在任意大的代码块周围包装 ew 。发生什么是首先,扩展的代码是从它生成的,就像你自己写它,然后代码被执行一样。对于函数定义的情况,比如上面的 f ,我们可以确定代码生成发生在编译时,所以在函数中使用时避免任何运行时间开销后来,如果函数经常被调用,这可能是很实际的。

这种方法的另一个优点是它的可组合性:你可以想出很多语法扩展,他们写了一个类似于 ew 的函数。然后,假设这些自定义的代码转换函数不相互矛盾,您可以简单地编写(嵌套)它们,以获得累积效果。从某种意义上说,通过这种方式,您可以创建一个自定义代码生成器,它可以从一些Mathematica表达式中生成有效的Mathematica代码,这些表达式代表您的自定义语言中的程序,您可以使用这些方法在Mathematica中创建这些程序。



编辑



在写作 expandWith 时,我使用迭代规则应用程序避免处理评估控制,这可能是一团糟。然而,对于那些感兴趣的人来说,这里是一个版本,它对未经评估的代码段做了一些明确的工作。

 清除[expandWithAlt]; 
expandWithAlt [heldCode_Hold]:=
模块[{myHold},
SetAttributes [myHold,HoldAll];
heldCode //。 HoldPattern [With [{Set [{vars__},{vals__}]},body_]]:>
[[eval =
(Thread [未评估[Hold [vars] = Hold [vals]],Hold] /。
保留[decl ___]:> myHold [With [{decl },body]])},
eval /;真] //。 myHold [x_]:> x]

我发现它比第一个复杂得多。


The usage message for Set reminds us that multiple assignments can easily be made across two lists, without having to rip anything apart. For example:

Remove[x1, x2, y1, y2, z1, z2];
{x1, x2} = {a, b}

Performs the assignment and returns:

{a, b}

Thread, commonly used to generate lists of rules, can also be called explicitly to achieve the same outcome:

Thread[{y1, y2} = {a, b}]
Thread[{z1, z2} -> {a, b}]

Gives:

{a, b}
{z1 -> a, z2 -> b}

However, employing this approach to generate localized constants generates an error. Consider this trivial example function:

Remove[f];
f[x_] :=
 With[{{x1, x2} = {a, b}},
  x + x1 + x2
  ]
f[z]

Here the error message:

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."

The error message documentation (ref/message/With/lvw), says in the 'More Information' section that, "This message is generated when the first element in With is not a list of assignments to symbols." Given this explanation, I understand the mechanics of why my assignment failed. Nonetheless, I'm puzzled and wondering if this is necessary restriction by WRI, or a minor design oversight that should be reported.

So here's my question:

Can anyone shed some light on this behavior and/or offer a workaround? I experimented with trying to force Evaluation, without luck, and I'm not sure what else to try.

解决方案

What you request is tricky. This is a job for macros, as already exposed by the others. I will explore a different possibility - to use the same symbols but put some wrappers around the code you want to write. The advantage of this technique is that the code is transformed "lexically" and at "compile-time", rather than at run-time (as in the other answers). This is generally both faster and easier to debug.

So, here is a function which would transform the With with your proposed syntax:

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]

Note that this operates on held code. This has the advantage that we don't have to worry about possible evaluation o the code neither at the start nor when expandWith is finished. Here is how it works:

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

This is, however, not very convenient to use. Here is a convenience function to simplify this:

ew = Function[code, ReleaseHold@expandWith@Hold@code, HoldAll]

We can use it now as:

In[47]:= ew@With[{{x1,x2}={a,b}},x+x1+x2]
Out[47]= a+b+x

So, to make the expansion happen in the code, simply wrap ew around it. Here is your case for the function's definition:

Remove[f];
ew[f[x_] := With[{{x1, x2} = {a, b}}, x + x1 + x2]]

We now check and see that what we get is an expanded definition:

?f
Global`f
f[x_]:=With[{x2=b,x1=a},x+x1+x2]

The advantage of this approach is that you can wrap ew around an arbitrarily large chunk of your code. What happens is that first, expanded code is generated from it, as if you would write it yourself, and then that code gets executed. For the case of function's definitions, like f above, we cansay that the code generation happens at "compile-time", so you avoid any run-time overhead when usin the function later, which may be substantial if the function is called often.

Another advantage of this approach is its composability: you can come up with many syntax extensions, and for each of them write a function similar to ew. Then, provided that these custom code-transforming functions don't conlict with each other, you can simply compose (nest) them, to get a cumulative effect. In a sense, in this way you create a custom code generator which generates valid Mathematica code from some Mathematica expressions representing programs in your custom languuage, that you may create within Mathematica using these means.

EDIT

In writing expandWith, I used iterative rule application to avoid dealing with evaluation control, which can be a mess. However, for those interested, here is a version which does some explicit work with unevaluated pieces of code.

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]

I find it considerably more complicated than the first one though.

这篇关于生成本地化变量时出错(作为常量)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆