在我写的一个模块中(用于开发和测试阶段),我有很多Print["Messages"]
。我有两个问题:
- 打印这样的“调试”消息的最佳实践是什么?
- 有没有一种方法可以调用该模块,使得消息不会被打印出来?例如,在从另一个模块调用该模块时,我希望不看到第一个模块的所有输出。
在我写的一个模块中(用于开发和测试阶段),我有很多Print["Messages"]
。我有两个问题:
我喜欢把全局变量的名称前缀加上$
,而将函数命名时则不加前缀,因此我会这样写:
debugPrint[args___] := $debugPrintFunction[args]
$debugPrintFunction = Print[##] &
那么你可以像现在使用Print
一样使用debugPrint
。当你想要摆脱调试信息时,只需取消设置变量即可:
$debugPrintFunction = .
这种方式有一些优势。其中之一是您可以使用Block
在本地切换调试开关:
In[1]:= foo[x_] := (debugPrint[x]; x+1)
In[2]:= foo[3]
3
Out[2]= 4
In[3]:= Block[{$debugPrintFunction}, foo[3]
Out[3]= 4
您甚至可以在本地使$debugPrintFunction
执行其他操作,例如对于Reap
要拾取的值进行Sow
,或将调试消息重定向到其他位置,比如
strm = OpenWrite["your/log/path/here", InputForm];
Block[{$debugPrintFunction = Write[strm, ##]},
foo[3]];
Close[strm];
如果明智地使用由Block
提供的动态作用域,可以相对安全和可控地使用全局变量。
我之前的代码使用了Pillsy描述的方法。
最近我使用了一个通常没有任何规则的头部,例如:
...
debugPrint[expr]
...
Attributes[PrintDebug]={HoldAll}
PrintDebug[expr_] := Block[{debugPrint = Print}, expr]
那么当我想查看调试输出时,我可以将PrintDebug包装在输入周围:
PrintDebug[MyFunction[1,2,3]]
MyFunction[1,2,3] // PrintDebug
我发现后缀形式更容易添加/删除,并且更好地保留主函数的焦点。
我通常会在我的函数中安装一个冗长选项,如果需要调试,则可以打开/关闭该选项。请注意,在函数内指定Verbose的默认值可以控制是否打印信息。
In[5]:= func1[arg_, opts___] := Module[{verbose},
verbose = Verbose /. {opts} /. {Verbose -> True};
If[verbose, Print["Verbosing function1: arg is ", arg]];
arg
];
func2[arg_, opts___] := Module[{verbose},
verbose = Verbose /. {opts} /. {Verbose -> False};
func1[arg, Verbose -> verbose]
];
In[7]:= func1[123]
During evaluation of In[7]:= Verbosing function1: arg is 123
Out[7]= 123
In[8]:= func2[456]
Out[8]= 456
In[9]:= func1[123, Verbose -> False]
Out[9]= 123
In[10]:= func2[456, Verbose -> True]
During evaluation of In[10]:= Verbosing function1: arg is 456
Out[10]= 456
Options [func1,Verbose -> ...]
标题,然后从函数内部访问选项),但这不是重点。debugPrint::msg = "Debug message: `1`";
debugPrint[msg_] := Message[debugPrint::msg, msg]
使用如下函数:
debugPrint["hello"]
关闭或打开此类消息:
Off[debugPrint::msg]
On[debugPrint::msg]
有不同级别的调试信息。粗略级别和详细级别。粗略级别仅在进入函数和退出函数时打印一条消息。
我在用户界面上有一个按钮可以用来开启/关闭调试(如果您正在进行基于UI的程序,否则它将在代码中)。
使用单独的调试函数,调试消息会通过该函数进行输出。在此函数中,您可以在打印之前添加时间戳等内容(也可以控制是否要将消息从一个地方写入文本文件)。其余部分的代码只需使用该调试函数呼叫要打印的消息即可。我现在将所有内容都打印到控制台而不是当前笔记本。
每个调试消息都以调用它的函数名开头。
如果要在模块级别控制调试,我所做的就是简单地在该模块内部设置一个局部调试标志,并在每次想要调试该特定模块时开启/关闭该标志。这个局部调试标志接管了全局调试标志的设置。这样,如果需要,我可以仅调试一个模块,而不是整个代码。
有很多其他的方法来自定义所有这些。但我发现,如果我在一开始花更多的时间来制作一个良好的调试消息系统,那么当需要找到bug时,它会非常有帮助。
以下是一些有用的链接
http://reference.wolfram.com/mathematica/guide/TuningAndDebugging.html
工作台调试器(如果您知道如何使用它来调试Manipulate和Dynamics,请告诉我)
http://www.wolfram.com/products/workbench/features/debug.html
Evaluation -> Debugger
- Szabolcs
Assert
,它会在测试失败时打印消息,并且可以被关闭。 - Yaroslav Bulatov