切(!)vs返回

时间:2014-11-07 14:59:18

标签: prolog prolog-cut

我正在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";
}

上述功能是否完全相同?

4 个答案:

答案 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中编程

Art of Prolog Cover

编辑注意:您的样本谓词

pred(X) :- 
   X = 1 -> do this , ! ; else do that ,
   write('x =/= 1')
   .

有一些问题。

  • 首先,就像C或C#或andor运算符具有不同优先级的其他过程语言一样,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 
   ).