Mathematica 作业中不需要的评估:为什么会发生以及如何在包加载期间对其进行调试? [英] Unwanted evaluation in assignments in Mathematica: why it happens and how to debug it during the package-loading?

查看:18
本文介绍了Mathematica 作业中不需要的评估:为什么会发生以及如何在包加载期间对其进行调试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个无法正确加载的(大)包.这发生在我更改了一行代码之后.当我尝试加载包(有需求)时,包开始加载,然后 setdelayed 定义之一活跃起来"(即以某种方式评估),被困在几行之前加载的错误捕获例程中,包加载中止.
带有 abort 的错误捕获例程正在完成它的工作,除了它不应该在包加载阶段首先被调用.错误消息表明错误的参数实际上是我在几行后在 setdelayed 定义的 lhs 上使用的模式表达式.

I am developing a (large) package which does not load properly anymore. This happened after I changed a single line of code. When I attempt to load the package (with Needs), the package starts loading and then one of the setdelayed definitions "comes alive" (ie. Is somehow evaluated), gets trapped in an error trapping routine loaded a few lines before and the package loading aborts.
The error trapping routine with abort is doing its job, except that it should not have been called in the first place, during the package loading phase. The error message reveals that the wrong argument is in fact a pattern expression which I use on the lhs of a setdelayed definition a few lines later.

像这样:

……Some code lines

Changed line of code 

g[x_?NotGoodQ]:=(Message[g::nogood, x];Abort[])

……..some other code lines

g/: cccQ[g[x0_]]:=True

当我尝试加载包时,我得到:

When I attempt to load the package, I get:

g::nogood: Argument x0_ is not good

如您所见,传递的参数是一个模式,它只能来自上面的代码行.

As you see the passed argument is a pattern and it can only come from the code line above.

我试图找到这种行为的原因,但到目前为止我一直没有成功.所以我决定使用强大的 Workbench 调试工具.

I tried to find the reason for this behavior, but I have been unsuccessful so far. So I decided to use the powerful Workbench debugging tools .

我想逐步(或使用断点)查看加载包时会发生什么.我对 WB 还不太熟悉,但似乎,使用 Debug as...,首先加载包,然后最终使用断点等进行调试.我的问题是包甚至没有完全加载!并且在加载包之前设置的任何断点似乎都无效.

I would like to see step by step (or with breakpoints) what happens when I load the package. I am not yet too familiar with WB, but it seems that ,using Debug as…, the package is first loaded and then eventually debugged with breakpoints, ect. My problem is that the package does not even load completely! And any breakpoint set before loading the package does not seem to be effective.

所以……2个问题:

  1. 谁能解释为什么这些代码行在包加载过程中活跃起来"?(据我所知,包中没有留下明显的语法错误或代码片段)
  2. 谁能解释一下如何(如果)可以检查/调试在 WB 中加载时打包代码?

感谢您的帮助.

编辑

根据 Leonid 的回答并使用他的 EvenQ 示例:我们可以避免使用 Holdpattern,只需在 g 的下值之前定义 g 的上值

In light of Leonid's answer and using his EvenQ example: We can avoid using Holdpattern simply by definying upvalues for g BEFORE downvalues for g

notGoodQ[x_] := EvenQ[x];
Clear[g];
g /: cccQ[g[x0_]] := True
g[x_?notGoodQ] := (Message[g::nogood, x]; Abort[])

现在

?g

Global`g

cccQ[g[x0_]]^:=True



g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])



In[6]:= cccQ[g[1]]

Out[6]= True

同时

In[7]:= cccQ[g[2]]

During evaluation of In[7]:= g::nogood: -- Message text not found -- (2)

Out[7]= $Aborted

所以...一般规则:

编写函数g时,先为g定义上值,然后为g定义下值,否则使用Holdpattern

When writing a function g, first define upvalues for g, then define downvalues for g, otherwise use Holdpattern

您可以订阅此规则吗?

Leonid 说使用 Holdpattern 可能表明设计可以改进.除了上面指出的解决方案,如何改进上面的小代码的设计,或者更好的是,在处理上值时一般?

Leonid says that using Holdpattern might indicate improvable design. Besides the solution indicated above, how could one improve the design of the little code above or, better, in general when dealing with upvalues?

感谢您的帮助

推荐答案

先不说 WB(实际上并不是回答您的问题所必需的)- 该问题似乎仅基于在分配期间如何评估表达式的方式而有一个直截了当的答案.下面是一个例子:

Leaving aside the WB (which is not really needed to answer your question) - the problem seems to have a straightforward answer based only on how expressions are evaluated during assignments. Here is an example:

In[1505]:= 
notGoodQ[x_]:=True;
Clear[g];
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])

In[1509]:= g/:cccQ[g[x0_]]:=True

During evaluation of In[1509]:= g::nogood: -- Message text not found -- (x0_)
Out[1509]= $Aborted

为了让它起作用,我特意为notGoodQ 定义了总是返回True.现在,为什么在通过 TagSetDelayed 赋值的过程中对 g[x0_] 求值?答案是,虽然 TagSetDelayed(以及 SetDelayed)在赋值 h/:f[h[elem1,...,elemn]​​] 中:=... 不应用 f 可能具有的任何规则,它将评估 h[elem1,...,elem2],以及 <代码>f.下面是一个例子:

To make it work, I deliberately made a definition for notGoodQ to always return True. Now, why was g[x0_] evaluated during the assignment through TagSetDelayed? The answer is that, while TagSetDelayed (as well as SetDelayed) in an assignment h/:f[h[elem1,...,elemn]]:=... does not apply any rules that f may have, it will evaluate h[elem1,...,elem2], as well as f. Here is an example:

In[1513]:= 
ClearAll[h,f];
h[___]:=Print["Evaluated"];

In[1515]:= h/:f[h[1,2]]:=3

During evaluation of In[1515]:= Evaluated
During evaluation of In[1515]:= TagSetDelayed::tagnf: Tag h not found in f[Null]. >>
Out[1515]= $Failed  

TagSetDelayedHoldAll 的事实并不意味着它不对它的参数求值 - 它只意味着参数到达它时未求值,无论它们是否将根据 TagSetDelayed 的语义进行评估(我在上面简要介绍过).SetDelayed 也是如此,因此不评估其参数"的常用声明在字面上是不正确的.更正确的说法是它接收未评估的参数并以特殊方式评估它们 - 不评估 r.h.s,而对于 l.h.s.,评估头部和元素但不对头部应用规则.为避免这种情况,您可以将内容包装在 HoldPattern 中,如下所示:

The fact that TagSetDelayed is HoldAll does not mean that it does not evaluate its arguments - it only means that the arguments arrive to it unevaluated, and whether or not they will be evaluated depends on the semantics of TagSetDelayed (which I briefly described above). The same holds for SetDelayed, so the commonly used statement that it "does not evaluate its arguments" is not literally correct. A more correct statement is that it receives the arguments unevaluated and does evaluate them in a special way - not evaluate the r.h.s, while for l.h.s., evaluate head and elements but not apply rules for the head. To avoid that, you may wrap things in HoldPattern, like this:

Clear[g,notGoodQ];
notGoodQ[x_]:=EvenQ[x];
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])
g/:cccQ[HoldPattern[g[x0_]]]:=True;

这就通过了.下面是一些用法:

This goes through. Here is some usage:

In[1527]:= cccQ[g[1]]
Out[1527]= True

In[1528]:= cccQ[g[2]]
During evaluation of In[1528]:= g::nogood: -- Message text not found -- (2)
Out[1528]= $Aborted

但是请注意,在进行定义时,左侧需要 HoldPattern 通常表明您头脑中的表达式也可能在函数调用期间求值,这可能会破坏您的代码.这是我的意思的一个例子:

Note however that the need for HoldPattern inside your left-hand side when making a definition is often a sign that the expression inside your head may also evaluate during the function call, which may break your code. Here is an example of what I mean:

In[1532]:= 
ClearAll[f,h];
f[x_]:=x^2;
f/:h[HoldPattern[f[y_]]]:=y^4;

这段代码试图捕捉像 h[f[something]] 这样的情况,但它显然会失败,因为 f[something] 会在求值到达 <代码>h:

This code attempts to catch cases like h[f[something]], but it will obviously fail since f[something] will evaluate before the evaluation comes to h:

In[1535]:= h[f[5]]
Out[1535]= h[25]

对我来说,l.h.s. 上需要 HoldPattern.表明我需要重新考虑我的设计.

For me, the need for HoldPattern on the l.h.s. is a sign that I need to reconsider my design.

编辑

关于在 WB 中加载期间的调试,您可以做的一件事(IIRC,现在无法检查)是使用良好的旧打印语句,其输出将出现在 WB 的控制台中.个人感觉很少需要调试器来达到这个目的(加载时调试包)

Regarding debugging during loading in WB, one thing you can do (IIRC, can not check right now) is to use good old print statements, the output of which will appear in the WB's console. Personally, I rarely feel a need for debugger for this purpose (debugging package when loading)

编辑 2

回应问题中的

关于定义的顺序:是的,你可以这样做,它解决了这个特定的问题.但是,一般来说,这不是健壮的,我不会认为它是一个很好的通用方法.很难对手头的案例给出明确的建议,因为它有点脱离上下文,但在我看来,在这里使用 UpValues 是不合理的.如果这样做是为了错误处理,还有其他方法 在不使用 UpValues 的情况下做到这一点.

Regarding the order of definitions: yes, you can do this, and it solves this particular problem. But, generally, this isn't robust, and I would not consider it a good general method. It is hard to give a definite advice for a case at hand, since it is a bit out of its context, but it seems to me that the use of UpValues here is unjustified. If this is done for error - handling, there are other ways to do it without using UpValues.

通常,UpValues 最常用于以安全的方式重载某些函数,而不会对被重载的函数添加任何规则.一个建议是避免将 UpValues 与也有 DownValues 的头部相关联,并且可能会评估 - 通过这样做,您开始与评估器玩游戏,最终会失败.最安全的方法是将 UpValues 附加到惰性符号(头部、容器)上,这些符号通常表示您要在其上重载给定函数的类型"对象.

Generally, UpValues are used most commonly to overload some function in a safe way, without adding any rule to the function being overloaded. One advice is to avoid associating UpValues with heads which also have DownValues and may evaluate -by doing this you start playing a game with evaluator, and will eventually lose. The safest is to attach UpValues to inert symbols (heads, containers), which often represent a "type" of objects on which you want to overload a given function.

关于我对 HoldPattern 存在的评论表明设计不佳.HoldPattern 肯定有 合法的用途,比如这个(有点人为的):

Regarding my comment on the presence of HoldPattern indicating a bad design. There certainly are legitimate uses for HoldPattern, such as this (somewhat artificial) one:

In[25]:= 
Clear[ff,a,b,c];
ff[HoldPattern[Plus[x__]]]:={x};
ff[a+b+c]

Out[27]= {a,b,c} 

这里是有道理的,因为在许多情况下 Plus 仍然是未计算的,并且以其未计算的形式很有用 - 因为人们可以推断它代表一个总和.我们需要 HoldPattern 是因为 Plus 是在单个参数上定义的,并且因为模式恰好是单个参数(即使它通常描述了多个参数)定义.因此,我们在这里使用 HoldPattern 来防止将模式视为普通参数,但这与 Plus 的预期用例大不相同.在这种情况下(我们确信该定义对于预期的用例会正常工作),HoldPattern 很好.注意 b.t.w.,这个例子也很脆弱:

Here it is justified because in many cases Plus remains unevaluated, and is useful in its unevaluated form - since one can deduce that it represents a sum. We need HoldPattern here because of the way Plus is defined on a single argument, and because a pattern happens to be a single argument (even though it describes generally multiple arguments) during the definition. So, we use HoldPattern here to prevent treating the pattern as normal argument, but this is mostly different from the intended use cases for Plus. Whenever this is the case (we are sure that the definition will work all right for intended use cases), HoldPattern is fine. Note b.t.w., that this example is also fragile:

In[28]:= ff[Plus[a]]
Out[28]= ff[a]

大部分情况下仍然可以的原因是通常我们不会在单个参数上使用 Plus.

The reason why it is still mostly OK is that normally we don't use Plus on a single argument.

但是,还有第二组情况,其中通常提供的参数结构与用于定义的模式结构相同.在这种情况下,赋值期间的模式评估表明在函数调用期间将使用实际参数进行相同的评估.您的使用属于这一类.我对设计缺陷的评论是针对这种情况的 - 您可以阻止模式评估,但您必须同时阻止参数评估,才能使这项工作发挥作用.并且针对未完全评估的表达式的模式匹配是脆弱的.此外,该函数不应该为参数假设一些额外的条件(超出它可以进行类型检查的条件).

But, there is a second group of cases, where the structure of usually supplied arguments is the same as the structure of patterns used for the definition. In this case, pattern evaluation during the assignment indicates that the same evaluation will happen with actual arguments during the function calls. Your usage falls into this category. My comment for a design flaw was for such cases - you can prevent the pattern from evaluating, but you will have to prevent the arguments from evaluating as well, to make this work. And pattern-matching against not completely evaluated expression is fragile. Also, the function should never assume some extra conditions (beyond what it can type-check) for the arguments.

这篇关于Mathematica 作业中不需要的评估:为什么会发生以及如何在包加载期间对其进行调试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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