如果我有一个用作命令行工具的程序,我的调试选项是什么?
为了便于说明,我们假设程序看起来像这样。
清单do_stuff.pl
:
main :-
current_prolog_flag(argv, Argv),
do_stuff(Argv),
halt.
main :-
halt(1).
使用SWI-Prolog,我可以使用以下命令进行编译:
swipl --goal=main -o do_stuff -c do_stuff.pl
我可以通过简单地调用来运行它
$ ./do_stuff foo bar baz
就目前而言,如果do_stuff/1
失败,它将以1退出。如何查看失败的第一个(最早,最深)目标?甚至更好的是整个回溯?我以为我应该可以使用debug
和leash
,例如:
main :-
current_prolog_flag(argv, Argv),
debug, leash(+fail),
do_stuff(Argv),
halt.
...但是我没有尝试过任何有效的方法。
我唯一的半信半疑的想法是对我希望确定性地成功但没有成功的每个谓词抛出错误。这当然是可行的,但似乎有点过头了?
用作命令行工具的程序(通常)旨在运行一次,获取其参数,读取其输入,写入输出。在这种情况下,失败是什么意思?我的解释是,意外失败是程序中的错误。
单元测试可能会有所帮助(孤立地测试谓词);但是,根据定义,这将不会帮助程序员因对问题,范围或工具缺乏理解而导致的错误。仅在具有实际输入的情况下运行程序会捕获此类错误。
因此,在上述示例中,如果某个用例导致do_stuff/1
失败,并且程序以非零代码退出,那么程序员在确定哪个谓词失败时有哪些选择?
评论中链接的答案提供了一种解决方案。但是(如果我理解正确的话)这确实需要程序员系统地检查执行流程,直到找到令人讨厌的谓词调用为止。
这正是我希望避免的。
在Prolog中,失败是非常不寻常的事情-与更多面向命令的语言相比。从第1天开始,它就引起了人们的兴趣。实际上,即使在Prolog 0(Prolog I之前的版本)中,在trace选项旁边也有ECRIRE
一个IMPASSES
仅显示失败的特殊选项。
后来,MirelleDucassé进行了特别的工作,试图自动找出如何解释故障。
失败的奇异之处在于,它们不一定表示出了问题。但是有时候是。
我会说,如何理解失败有两个不同的方向。第一个是程序性的,第二个是声明性的。
在许多程序中,我(@)/1
常常表示我希望一个目标总是能够成功。多亏了运算符的声明,这只是一个额外的字符:
...,
@goal_aux_togoalaux_spec(OQuery, FVect0, Query, Spec),
...
如果目标失败,则会发出错误。嵌套异常也要记录下来,这一点也很重要。如果时间紧迫,@
必须将其删除。但是,我只计算了120kLOP中的〜400。
请注意,这@
对于具有多个答案的目标也很有效。像@member(1,[X,Y])
。
该技术对于实际的已编程程序效果很好。考虑准备一个失败切片(上面的例子)。在那里,您处于主要思考的情况:这是一个程序,什么是合适的切片?在这种情况下,答案是:“没有没有片”将不会成为答案。您真的希望它能够永远成功。如果您没有这样的模式程序,则通常可以通过执行固定性来转换现有的未修改程序:
p(X, Y) :-
wellformed(X),
@p_old(X, Yc),
Yc = Y.
该技术在纯粹的关系式,声明性代码中迅速失去了吸引力。以一个最近的例子中的斑马拼图。在那里,@
除了第一个目标以外,几乎不可能添加-。在这种情况下,需要一种更具声明性的方法,如下所示。
对于更复杂的问题,请@
不要做得很好。而是需要程序修改/切片。人们将需要通过添加前缀来概括程序*
。在SO上请参见此答案以获取此类调试会话的集合,这些调试会话手动使用此技术。该技术的重点在于,在确定最大泛化时,您不必了解程序的真实含义。您只需要注意失败的目标。
理想情况下,将自动产生这种概括。但是,有很多障碍。一方面,它们仅适用于纯单调代码(实际上,这是为什么人们应该坚持使用这种代码的一种很好的动力)。因此,首先必须对现有代码进行分析和分类。如果系统不遵循并随机更改其行为(例如您提到的系统),则更加困难。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句