Mathematica中的任务中不需要的评估:为什么会发生这种情况以及如何在程序包加载期间调试它?

mag*_*gma 10 wolfram-mathematica workbench

我正在开发一个(大)包,不再正常加载.这发生在我更改了一行代码之后.当我尝试加载包(使用Needs)时,包开始加载,然后其中一个setdelayed定义"活跃起来"(即以某种方式进行评估),被困在错误捕获例程之前加载几行和包加载中止.
带有中止的错误捕获例程正在执行其工作,除了在包加载阶段不应该首先调用它.错误消息显示错误的参数实际上是一个模式表达式,我在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
Run Code Online (Sandbox Code Playgroud)

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

g::nogood: Argument x0_ is not good
Run Code Online (Sandbox Code Playgroud)

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

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

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

所以... 2个问题:

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

感谢您的任何帮助.

编辑

根据列昂尼德的回答并使用他的EvenQ例子:我们可以避免使用Holdpattern简单地通过定义up值来获得g之前的下行值

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

现在

?g

Global`g

cccQ[g[x0_]]^:=True



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



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

Out[6]= True
Run Code Online (Sandbox Code Playgroud)

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

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

Out[7]= $Aborted
Run Code Online (Sandbox Code Playgroud)

所以...一般规则:

在编写函数g时,首先定义g的upvalues,然后定义g的downvalues,否则使用 Holdpattern

你能订阅这个规则吗?

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

谢谢您的帮助

Leo*_*rin 13

抛开WB(这不是真正需要回答你的问题) - 问题似乎只是根据在分配期间如何评估表达式而得到一个简单的答案.这是一个例子:

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

为了使它工作,我故意定义为notGoodQ永远返回True.现在,为什么g[x0_]在任务期间进行了评估TagSetDelayed?答案是,虽然任务中的TagSetDelayed(以及SetDelayed)h/:f[h[elem1,...,elemn]]:=...不适用任何f可能具有的规则,但它将评估h[elem1,...,elem2],以及f.这是一个例子:

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

这事实上TagSetDelayedHoldAll并不意味着它不评估其参数-它只是意味着参数到达其未计算的,而不管它们是否将被评估依赖于语义TagSetDelayed(我以上简述).同样适用SetDelayed,因此常常使用它"不评估其参数"的说法在字面上是不正确的.更正确的说法是它接收未评估的参数并以特殊方式评估它们 - 不评估rhs,而对于lhs,评估head和elements但不应用head的规则.为避免这种情况,您可以将内容包装起来HoldPattern,如下所示:

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

这经历了.这是一些用法:

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

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

In[1532]:= 
ClearAll[f,h];
f[x_]:=x^2;
f/:h[HoldPattern[f[y_]]]:=y^4;
Run Code Online (Sandbox Code Playgroud)

此代码尝试捕获类似的情况h[f[something]],但显然会失败,因为f[something]在评估之前将进行评估h:

In[1535]:= h[f[5]]
Out[1535]= h[25]
Run Code Online (Sandbox Code Playgroud)

对我来说,HoldPattern对lhs的需求是我需要重新考虑我的设计的标志.

编辑

关于在WB中加载期间的调试,你可以做的一件事(IIRC,现在无法检查)是使用好的旧打印语句,其输出将出现在WB的控制台中.就个人而言,我很少觉得需要调试器用于此目的(加载时调试包)

编辑2

回答问题中的编辑:

关于定义的顺序:是的,你可以这样做,它解决了这个特殊的问题.但是,一般来说,这并不健全,我不认为这是一个很好的通用方法.对于手头的案例很难给出明确的建议,因为它有点超出其背景,但在我看来,UpValues这里的使用是不合理的.如果这是为了错误处理,那么还有 其他方法可以不使用UpValues.

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

关于我对存在HoldPattern表明设计不良的评论.当然合法的用途HoldPattern,比如这个(有些人为的):

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

Out[27]= {a,b,c} 
Run Code Online (Sandbox Code Playgroud)

在这里它是合理的,因为在许多情况下Plus仍然没有评估,并且在其未评估的形式中是有用的 - 因为可以推断它代表一个总和.我们需要HoldPattern这里因为Plus在单个参数上定义的方式,并且因为在定义期间模式碰巧是单个参数(即使它通常描述多个参数).因此,我们HoldPattern在这里使用以防止将模式视为正常参数,但这与预期的用例大不相同Plus.只要是这种情况(我们确定该定义适用于预期的用例),HoldPattern就可以了.注意顺便说一下,这个例子也很脆弱:

In[28]:= ff[Plus[a]]
Out[28]= ff[a]
Run Code Online (Sandbox Code Playgroud)

它仍然大多数情况的原因是通常我们不使用Plus单个参数.

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

  • IIRC代表:"如果我记得/正确回忆".我刚刚发现了它的含义 (2认同)