3

我正在 Prolog 中开发一个谓词,它有可能在它结束之前终止。出于这个原因,我一直在寻找类似于return;(C++) 的命令。我使用了 acut !但我怀疑它的字面意思是什么,以及它是否确实做了什么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

4 回答 4

7

Prolog 的执行机制与传统的命令式语言没有直接对应关系。因此,任何类比都会使您走上死路。

在您的示例中,剪切没有任何效果:(->)/2单独的已经排除了Else分支。从某种意义上说,它做了一个“微小”If的切割和替代。是否会有另一个子句pred/1,您的削减也会排除该分支。

Prolog 的执行机制要复杂得多。如果您坚持在命令式语言中进行类比,请考虑迭代器。cut 导致 cut 范围内的所有迭代器在 next 上产生 done next。所以它有点像break. 有些。但仅限于首先支持迭代器的语言。

如果你想学习 Prolog,不要试图从这些(一半)破碎的类比中发展你的概念。

最好先想象一下 Prolog 谓词所描述的关系,并以此近似谓词的含义。程序概念将一一适应。

于 2014-11-07T15:12:06.373 回答
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。

于 2014-11-07T16:19:57.973 回答
3

正如所指出的,Prolog 不能很好地映射到程序性思维。

我找到了将 Prolog 程序及其“数据库”视为树(森林?)的最佳方式。这个类比有点粗略,因为图包含循环(递归)。

当您要求 prolog 引擎确定特定断言(谓词)的真假时,它开始使用统一(模式匹配)对树进行深度优先、从左到右的遍历来指导遍历。当遍历到达叶节点时,谓词为true。在回溯时,它... 回溯并继续树行走。当没有更多可到达的叶节点时,谓词失败。

Prolog 是一种描述性语言:您用谓词演算来描述成功的条件。然后,您只需让 Prolog 的推理引擎找到适用的解决方案。如果你试图将程序性的、命令式的思想硬塞到模型中,除了让事情变得比原本应该的更困难之外,根据我的经验,你几乎可以保证性能不佳。

我发现 Leon Sterling 和 Eliot Shapiro 的教科书The Art of Prolog比 Clocksin & Mellish 的Programming in Prolog更有价值,更有启发性和启发性。

Prolog 封面的艺术

编辑注意:您的示例谓词

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

有一些问题。

  • 首先,就像 C 或 C# 或其他过程语言一样,andandor运算符具有不同的优先级,因此表达式 likeif ( 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')
       .
    
  • 此外,!您的示例中的 cut ( ) 是不必要的,因为蕴含运算符的->行为就像涉及到一个 cut 一样。这个:

    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 . 
    

一般来说,如果你正在学习序言,我会说避免使用隐含运算符(和交替(逻辑 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) .

我认为它更容易理解正在发生的事情,因为它使选择点(及其消除)明确。

于 2014-11-07T18:17:32.823 回答
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 
   ).
于 2015-06-04T01:00:46.667 回答