11

我正在开发一个(大)包,它不再正确加载。这发生在我更改了一行代码之后。当我尝试加载包(需要)时,包开始加载,然后其中一个 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……,首先加载包,然后最终用断点等进行调试。我的问题是包甚至没有完全加载!并且在加载包之前设置的任何断点似乎都不起作用。

所以……2个问题:

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

感谢您的任何帮助。

编辑

根据 Leonid 的回答并使用他的 EvenQ 示例:我们可以Holdpattern通过在 g 的向下值之前为 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

你能订阅这个规则吗?

列昂尼德说,使用Holdpattern可能表明可改进的设计。除了上面指出的解决方案之外,如何改进上面的小代码的设计,或者更好的是,一般来说,在处理 upvalues 时?

感谢您的帮助

4

1 回答 1

14

撇开 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

为了让它发挥作用,我特意为notGoodQto always return做了一个定义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  

这并不意味着TagSetDelayedHoldAll不评估它的参数 - 它只意味着参数到达它时没有被评估,并且它们是否会被评估取决于TagSetDelayed(我在上面简要描述过)的语义。这同样适用于SetDelayed,因此它“不评估其参数”的常用陈述在字面上并不正确。更正确的说法是它接收未评估的参数并以特殊方式评估它们 - 不评估 rhs,而对于 lhs,评估头部和元素但不为头部应用规则。为避免这种情况,您可以将内容包装在 中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]

对我来说,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} 

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

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

它仍然大部分都可以的原因是通常我们不会Plus在单个参数上使用。

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

于 2011-09-14T00:16:17.800 回答