我正在Prolog中开发一个谓词,它有可能在它结束之前终止。
出于这个原因,我正在寻找类似于return;
(C ++)的命令。我使用了cut !
,但我对它的字面意义是什么感到怀疑,以及它是否与return;
完全相同。例如:
pred(X) :-
X = 1 -> do this, !; else do that,
write('x =/= 1').
void pred(int X) {
if (X = 1) {do this; return;} else do that;
cout << "x =/= 1";
}
上述功能是否完全相同?
答案 0 :(得分:7)
Prolog的执行机制与传统的命令式语言没有直接的对应关系。所以任何类比都会让你走上死路。
在您的示例中,剪切无效:仅(->)/2
已经排除了Else
分支。从某种意义上说,它在If
和另一种选择上做了“微小”切割。是否会有pred/1
的另一条款,你的裁员也会排除该分支。
Prolog的执行机制要复杂得多。如果你坚持使用命令式语言进行类比,请考虑迭代器。剪切导致剪切范围内的所有迭代器在下一个next
上生成完成。所以它有点像break
。有些。但只是在一种支持迭代器的语言中。
如果你想学习Prolog,不要试图用这些(一半)破坏的类比来发展你的观念。
最好通过想象Prolog谓词描述的关系和谓词的含义近似来开始。程序性概念将逐一适用。
答案 1 :(得分:3)
所以你有一些像这样的程序代码:
def foo():
if cond1:
handle_cond1()
return
if cond2:
handle_cond2()
return
do_other_stuff()
您可以在程序域中将其转换为没有显式返回,首先执行此操作:
def foo():
if cond1:
handle_cond1()
return
if cond2:
handle_cond2()
else:
do_other_stuff()
然后这样做:
def foo():
if cond1:
handle_cond1()
else:
if cond2:
handle_cond2()
else:
do_other_stuff()
一旦消除了return
语句,就可以将其转换为Prolog:
foo :-
cond1 ->
handle_cond1
; (cond2 ->
handle_cond2
; do_other_stuff
).
Prolog无法立即取得成功。 (你可以立即失败fail
)。您将不得不执行这样的转换以实现类似的流程。最重要的是遵循@false的建议并按照自己的条件学习Prolog。
答案 2 :(得分:3)
正如所指出的那样,Prolog并没有很好地映射到程序性思想。
我找到了将Prolog程序及其“数据库”视为树(森林?)的最佳方法。由于图表包含循环(递归),因此类比有点粗糙。
当你要求prolog引擎确定特定断言(谓词)的真或假时,它开始使用统一(模式匹配)对树进行深度优先,从左到右的遍历来指导遍历。当遍历到达叶节点时,谓词为true
。在回溯中,它......回溯并继续树行走。当没有更多可到达的叶节点时,谓词就会失败。
Prolog是一种描述性语言:您描述谓词演算方面的成功条件。然后,您只需让Prolog的推理引擎找到适用的解决方案。如果你试图将程序性的,强制性的思想运用到模型中,除了让事情变得比原本更困难之外,根据我的经验,你几乎可以保证表现不佳。
我发现Leon Sterling和Eliot Shapiro的教科书The Art of Prolog非常宝贵,比Clocksin&amp; amp;更具启发性和启发性。 Mellish的在Prolog中编程。
编辑注意:您的样本谓词
pred(X) :-
X = 1 -> do this , ! ; else do that ,
write('x =/= 1')
.
有一些问题。
首先,就像C或C#或and
和or
运算符具有不同优先级的其他过程语言一样,if ( a && b || c && d ) ...
这样的表达式可能不会绑定方式你认为它确实如此,由于运算符优先级,你的示例谓词可能没有做你认为它正在做的事情:正如所写,它绑定为
pred(X) :-
X=1 ->
( do_this , ! )
;
( do_that , write( 'x =/= 1' ) )
.
当你想要的时候
pred(X) :-
( X=1 ->
( do_this , ! )
;
do_that ,
) ,
write( 'x =/= 1' )
.
您需要使用括号来明确显示预期的绑定:
pred(X) :-
( X=1 ->
( do_this , ! )
;
do_that
),
write('x =/= 1')
.
此外,您的示例中的剪切(!
)是不必要的,因为隐含运算符->
的行为就像涉及到了剪切一样。这样:
foo(X) :-
truthy(X) ->
writeln('truthy!')
;
writeln('falsy')
.
与
几乎完全相同foo(X) :- truthy(X) , ! ,
writeln( 'truthy' ) .
foo(_) :- writeln( 'falsy' ) .
第三,你应该在头脑中使用统一和模式匹配。忽略write/1
,您的示例可能更有意义
pred(1) :- do_this , ! .
pred(X) :- do_that .
一般来说,如果你正在学习prolog,我会说避免使用蕴涵运算符(和替换(逻辑OR,';'/2
)一般。首选带有多个子句的显式谓词来表达选择。而不是某些东西像
foo(X) :- ( try_this(X) ; try_that(X) ; finally(X) ) .
喜欢
foo(X) :- try_this(X) .
foo(X) :- try_that(X) .
foo(X) :- finally(X) .
而不是暗示:
foo(X) :- X=1 -> try_this(X) ; try_that(X) .
喜欢这样的东西:
foo(1) :- ! , try_this(X) .
foo(X) :- try_that(X) .
我认为这样可以更容易地了解正在发生的事情,因为它使选择点(及其消除)明确。
答案 3 :(得分:2)
我想补充一点,拥有可靠的编码指南也可以帮助提高代码的可读性并避免代码脆弱。
从@DanielLyons代码开始:
foo :-
cond1 ->
handle_cond1
; (cond2 ->
handle_cond2
; do_other_stuff
).
在实践中,会出现多个嵌套的if-then-else结构的级联:“if-then-elseif-then-elseif-then-elseif-then -...- else”。
为了提高可读性,可以刷新代码布局并调整缩进级别:
foo :-
( cond1 -> handle_cond1
; cond2 -> handle_cond2
; do_other_stuff
).
每当代码行变得太宽时,可能更喜欢宽度稍微更高的样式:
foo :-
( cond1
-> handle_cond1
; cond2
-> handle_cond2
; do_other_stuff
).