我正在开发一个不再正常加载的(大)包。
这发生在我更改了一行代码之后。
当我尝试加载包(使用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
当我尝试加载包时,我得到:
g::nogood: Argument x0_ is not good
如您所见,传递的参数是一个模式,它只能来自上面的代码行。
我试图找到这种行为的原因,但到目前为止我还没有成功。 所以我决定使用功能强大的Workbench调试工具。
我想逐步(或断点)看到加载包时会发生什么。 我还不太熟悉WB,但似乎使用Debug as ...,首先加载包,然后最终使用断点调试,等等。 我的问题是包装甚至没有完全加载!在加载包之前设置的任何断点似乎都没有效果。
所以... 2个问题:
感谢您的帮助。
修改
根据列昂尼德的回答并使用他的EvenQ例子:
我们可以避免使用Holdpattern
简单地为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的upvalues,然后定义g的downvalues,否则使用Holdpattern
您可以订阅此规则吗?
Leonid说使用Holdpattern
可能表明设计可以改进。除了上面提到的解决方案之外,如何改进上面的小代码的设计,或者更好的是,在处理upvalues时更好?
感谢您的帮助
答案 0 :(得分: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
为了使其有效,我故意为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
TagSetDelayed
是HoldAll
的事实并不意味着它不会评估它的论点 - 它只意味着参数到达它没有评估,它们是否会被评估取决于TagSetDelayed
的语义(我在上面简要介绍过)。同样适用于SetDelayed
,因此它“不评估其参数”的常用语句在字面上是不正确的。更正确的说法是它接收未评估的参数并以特殊方式评估它们 - 不评估r.h.s,而对于l.h.s.,评估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;
这是经历的。这是一些用法:
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
通常表示头部内的表达式也可能在函数调用期间进行评估,这可能会破坏您的代码。这是我的意思的一个例子:
In[1532]:=
ClearAll[f,h];
f[x_]:=x^2;
f/:h[HoldPattern[f[y_]]]:=y^4;
此代码尝试捕获h[f[something]]
之类的案例,但显然会失败,因为f[something]
会在评估到h
之前评估:
In[1535]:= h[f[5]]
Out[1535]= h[25]
对我来说,l.h.上需要HoldPattern
。这是我需要重新考虑我的设计的标志。
修改强>
关于在WB中加载期间的调试,您可以做的一件事(IIRC,现在无法检查)是使用旧的打印语句,其输出将出现在WB的控制台中。就个人而言,我很少觉得需要调试器(加载时调试包)
编辑2
回答问题中的编辑:
关于定义的顺序:是的,你可以这样做,它解决了这个特殊的问题。但是,一般来说,这并不健全,我不认为这是一个很好的通用方法。很难对手头的案例提出明确的建议,因为它有点超出其背景,但在我看来,UpValues
在这里的使用是不合理的。如果这是为了进行错误处理,那么other ways可以在不使用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}
这是合理的,因为在许多情况下Plus
仍未评估,并且在其未评估的形式中有用 - 因为人们可以推断出它代表了一个总和。我们在这里需要HoldPattern
,因为在单个参数上定义Plus
的方式,并且因为在定义期间模式碰巧是单个参数(即使它通常描述多个参数)。因此,我们在这里使用HoldPattern
来防止将模式视为普通参数,但这与Plus
的预期用例大不相同。只要是这种情况(我们确定该定义适用于预期的用例),HoldPattern
就可以了。注意b.t.w.,这个例子也很脆弱:
In[28]:= ff[Plus[a]]
Out[28]= ff[a]
它仍然大部分正常的原因是通常我们不会在一个参数上使用Plus
。
但是,还有第二组案例,其中通常提供的参数的结构与用于定义的模式结构相同。在这种情况下,赋值期间的模式评估表明在函数调用期间将使用实际参数进行相同的评估。您的使用属于此类别。我对设计缺陷的评论是针对这种情况 - 你可以阻止模式进行评估,但是你必须阻止参数进行评估,才能使这项工作成功。并且针对未完全评估的表达的模式匹配是脆弱的。此外,函数不应该为参数假设一些额外的条件(超出它可以类型检查的范围)。