Mathematica 作业中不需要的评估:为什么会发生这种情况以及如何在包加载过程中对其进行调试?
我正在开发一个(大)包,它不再正确加载。 这是在我更改一行代码后发生的。 当我尝试加载包(需要)时,包开始加载,然后 setdelayed 定义之一“活跃起来”(即以某种方式进行评估),被困在之前加载几行的错误捕获例程中,并且包加载中止。
使用 abort 的错误捕获例程正在完成其工作,只是在包加载阶段不应该首先调用它。 错误消息表明错误的参数实际上是一个模式表达式,我稍后在 setdelayed 定义的 lhs 上使用它。
像这样的事情:
……Some code lines
Changed line of code
g[x_?NotGoodQ]:=(Message[g::nogood, x];Abort[])
……..some other code lines
g/: cccQ[g[x0_]]:=True
当我尝试加载包时,我得到:
g::nogood: Argument x0_ is not good
如您所见,传递的参数是一个模式,它只能来自上面的代码行。
我试图找到这种行为的原因,但到目前为止我还没有成功。 所以我决定使用强大的Workbench调试工具。
我想逐步(或使用断点)查看加载包时会发生什么。 我对WB还不太熟悉,但似乎使用Debug as...,首先加载包,然后最终使用断点等进行调试。 我的问题是该包甚至没有完全加载!并且在加载包之前设置的任何断点似乎都不起作用。
那么……两个问题:
- 有人能解释一下为什么这些代码行在包加载过程中“活跃起来”吗? (据我所知,包中没有明显的语法错误或代码片段)
- 任何人都可以解释一下如何(如果)可以检查/调试 WB 中加载包代码时?
感谢您的帮助。
编辑
根据 Leonid 的回答并使用他的 EvenQ 示例: 我们可以简单地通过在 g 的下值之前定义 g 的上值来避免使用 Holdpattern
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
您可以订阅此规则吗?
Leonid 表示,使用 Holdpattern
可能表明设计可以改进。除了上面指出的解决方案之外,在处理上值时,如何改进上面一小段代码的设计,或者更好的是?
感谢您的帮助
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.
Something like this:
……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.
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 .
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.
So…2 questions:
- can anybody please explain why these code lines "come alive" during package loading? (there are no obvious syntax errors or code fragments left in the package as far as I can see)
- can anybody please explain how (if) is possible to examine/debug
package code while being loaded in WB?
Thank you for any help.
Edit
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[])
Now
?g
Global`g
cccQ[g[x0_]]^:=True
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])
In[6]:= cccQ[g[1]]
Out[6]= True
while
In[7]:= cccQ[g[2]]
During evaluation of In[7]:= g::nogood: -- Message text not found -- (2)
Out[7]= $Aborted
So...general rule:
When writing a function g, first define upvalues for g, then define downvalues for g, otherwise use Holdpattern
Can you subscribe to this rule?
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?
Thank you for your help
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
撇开WB(实际上不需要回答你的问题) - 这个问题似乎有一个简单的答案,仅基于在作业期间如何评估表达式。下面是一个示例:
为了使其正常工作,我特意将
notGoodQ
定义为始终返回True
。现在,为什么在赋值期间通过TagSetDelayed
评估g[x0_]
?答案是,当TagSetDelayed
(以及SetDelayed
)在赋值h/:f[h[elem1,...,elemn]] 中时: =...
不应用f
可能具有的任何规则,它将评估h[elem1,...,elem2]
,以及 <代码>f。下面是一个示例:TagSetDelayed
为HoldAll
的事实并不意味着它不评估其参数 - 它仅意味着参数到达时未评估,并且无论是不是它们将被评估取决于TagSetDelayed
的语义(我在上面简要描述过)。对于SetDelayed
也是如此,因此常用的“不评估其参数”的说法实际上并不正确。更正确的说法是,它接收未评估的参数,并以特殊方式评估它们 - 不评估 rhs,而对于 lhs,评估 head 和元素,但不对 head 应用规则。为了避免这种情况,您可以将内容包装在HoldPattern
中,如下所示:This gos through。以下是一些用法:
但请注意,在进行定义时,左侧需要
HoldPattern
通常表明您头脑中的表达式也可能在函数调用期间进行计算,这可能会中断你的代码。这是我的意思的一个例子:此代码尝试捕获像
h[f[something]]
这样的情况,但它显然会失败,因为f[something]
将评估在评估进行到h
之前:对我来说,lhs 上需要
HoldPattern
表明我需要重新考虑我的设计。编辑
关于在WB中加载期间的调试,您可以做的一件事(IIRC,现在无法检查)是使用良好的旧打印语句,其输出将出现在WB的控制台中。就我个人而言,我很少觉得需要为此目的使用调试器(加载时调试包)
编辑2
响应问题中的编辑:
关于定义的顺序:是的,你可以这样做,并且它解决了这个特殊问题。但是,一般来说,这并不稳健,我不认为它是一个好的通用方法。很难针对手头的案例给出明确的建议,因为它有点脱离了上下文,但在我看来,这里使用
UpValues
是不合理的。如果这样做是为了错误处理,还有其他方法 无需使用UpValues
即可完成此操作。一般来说,
UpValues
最常用于以安全的方式重载某些函数,而不向被重载的函数添加任何规则。一个建议是避免将UpValues
与也具有DownValues
并且可能评估的头关联起来 - 通过这样做,您开始使用评估器玩游戏,并最终会失败。最安全的方法是将UpValues
附加到惰性符号(头、容器),这些符号通常表示您想要重载给定函数的对象的“类型”。关于我对
HoldPattern
存在的评论表明设计不好。HoldPattern
肯定有合法的用途,例如这个(有点人为的):这里它是合理的,因为在许多情况下
Plus
仍然未被评估,并且以其未评估的形式很有用 - 因为人们可以推断出它代表一个总和。我们在这里需要HoldPattern
,因为Plus
是在单个参数上定义的,并且模式恰好是单个参数(即使它通常描述多个参数)的定义。因此,我们在这里使用HoldPattern
来防止将模式视为普通参数,但这与Plus
的预期用例有很大不同。无论何时出现这种情况(我们确信该定义对于预期用例来说都可以正常工作),HoldPattern
就可以了。顺便说一句,请注意,这个示例也很脆弱:它仍然大部分正常的原因是我们通常不会在单个参数上使用
Plus
。但是,还有第二组情况,通常提供的参数的结构与用于定义的模式的结构相同。在这种情况下,赋值期间的模式评估表明在函数调用期间实际参数将发生相同的评估。您的使用情况属于此类。我对设计缺陷的评论是针对这种情况 - 您可以阻止模式评估,但您也必须阻止参数评估,才能使这项工作正常进行。并且针对未完全评估的表达式的模式匹配是脆弱的。此外,函数不应该为参数假设一些额外的条件(超出它可以类型检查的条件)。
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:
To make it work, I deliberately made a definition for
notGoodQ
to always returnTrue
. Now, why wasg[x0_]
evaluated during the assignment throughTagSetDelayed
? The answer is that, whileTagSetDelayed
(as well asSetDelayed
) in an assignmenth/:f[h[elem1,...,elemn]]:=...
does not apply any rules thatf
may have, it will evaluateh[elem1,...,elem2]
, as well asf
. Here is an example:The fact that
TagSetDelayed
isHoldAll
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 ofTagSetDelayed
(which I briefly described above). The same holds forSetDelayed
, 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 inHoldPattern
, like this:This goes through. Here is some usage:
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:This code attempts to catch cases like
h[f[something]]
, but it will obviously fail sincef[something]
will evaluate before the evaluation comes toh
:For me, the need for
HoldPattern
on the l.h.s. is a sign that I need to reconsider my design.EDIT
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)
EDIT 2
In response to the edit in the question:
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 usingUpValues
.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 associatingUpValues
with heads which also haveDownValues
and may evaluate -by doing this you start playing a game with evaluator, and will eventually lose. The safest is to attachUpValues
to inert symbols (heads, containers), which often represent a "type" of objects on which you want to overload a given function.Regarding my comment on the presence of
HoldPattern
indicating a bad design. There certainly are legitimate uses forHoldPattern
, such as this (somewhat artificial) one: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 needHoldPattern
here because of the wayPlus
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 useHoldPattern
here to prevent treating the pattern as normal argument, but this is mostly different from the intended use cases forPlus
. 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: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.