'if' in prolog?

76

在Prolog中有一种方法可以使用if语句,例如如果一个变量为0,则执行某些操作(向终端输出文本)。不需要else,但我找不到任何if的文档。


9
Prolog内置了if-then-else语法,但实际使用时它并不具备声明性。 - Willem Van Onsem
myFunc(Variable) :- Variable=0,print('OK, here is your text.'). - Topological Sort
10个回答

116

是的,在ISO Prolog中有这样一种控制结构,称为->。你可以像这样使用它:

( condition -> then_clause ; else_clause )

这是一个使用else-if语句链的示例:

(   X < 0 ->
    writeln('X is negative.  That's weird!  Failing now.'),
    fail
;   X =:= 0 ->
    writeln('X is zero.')
;   writeln('X is positive.')
)

请注意,如果省略 else 子句,则条件失败将意味着整个 if 语句也将失败。因此,我建议始终包括 else 子句(即使只是 true)。


18
在ISO中,控制结构实际上被称为(;)/2 - if-then-else (7.8.8),因为主要函子是(;)/2。这有点恼人,因为还有另一个具有相同主要函子的控制结构:(;)/2 - disjunction (7.8.6)。你可以这样理解它(if->then;else) == ((if->then);else)成立。 - false

55

一个标准的Prolog谓词可以实现这个功能。

   isfive(5). 

如果您使用 5 调用它,它将评估为 true,并在使用其他任何内容运行时失败(返回 false)。对于不相等,您可以使用 \=。

isNotEqual(A,B):- A\=B.

严格来说它并不是统一的,但它类似于不等于。

Learn Prolog Now 是一个很好的学习 Prolog 的网站。

编辑: 再添加一个例子。

isEqual(A,A). 

9
+1 强制使用逻辑编程风格而不是命令式编程风格 :-) - Agnius Vasiliauskas

32

Prolog谓词的“统一” -

因此,在命令式语言中,我会写

function bazoo(integer foo)
{
   if(foo == 5)
       doSomething();
   else
       doSomeOtherThing();
}

在Prolog中,我会这样写

bazoo(5) :-  doSomething.
bazoo(Foo) :- Foo =/= 5, doSomeOtherThing.

了解两种风格之后,实际上会更清晰易懂。
“当foo为5的特殊情况,我是bazoo”
“当foo不为5的正常情况,我是bazoo”


4
bazoo(5+0) 会默默失败,而 bazoo(5+1) 则会执行其他操作。 - false
如果你想简化数字,@false 可能应该否定 Foo is 5 - Pro Q
2
@ProQ:那将是 dif(Foo, 5) - false
1
这对于如何思考两种编程风格之间的区别非常有帮助。干杯。 - Ryan Cocuzzo

18

首先,让我们回顾一些经典的一阶逻辑:

“如果 P 那么 Q,否则 R”等价于“(P 且 Q) 或 (非_P 且 R)”。


如何在Prolog中表达类似于“if-then-else”的语句?

让我们以以下具体示例为例:

如果 X 是列表 [1,2] 的成员,则 那么 X 等于 2否则 X 等于 4

我们可以匹配上述模式(“如果 P 那么 Q 否则 R”)如果...

  • 条件 Plist_member([1,2],X)
  • 否定条件 non_Pnon_member([1,2],X)
  • 结果 QX=2,并且
  • 替代方案 RX=4

为了以纯粹的方式表示列表的成员关系,我们定义:

list_memberd([E|Es],X) :- ( E = X ; dif(E,X), list_memberd(Es,X) ).
non_member(Es,X) :- maplist(dif(X),Es).
(该代码段为Prolog语言,实现了列表中元素的查找和判断是否存在某个元素。)

让我们来看一下在Prolog中表达“if-then-else”的不同方法!

  1. (P,Q ; non_P,R)

    ?-      (list_memberd([1,2],X), X=2 ; non_member([1,2],X), X=4).
    X = 2 或 X = 4。
    ?- X=2, (list_memberd([1,2],X), X=2 ; non_member([1,2],X), X=4), X=2。
    X = 2 或 false。
    ?-      (list_memberd([1,2],X), X=2 ; non_member([1,2],X), X=4), X=2。
    X = 2 或 false。
    ?- X=4, (list_memberd([1,2],X), X=2 ; non_member([1,2],X), X=4), X=4。
    X = 4。
    ?-      (list_memberd([1,2],X), X=2 ; non_member([1,2],X), X=4), X=4。
    X = 4。
    

    正确性得分5/5。效率得分3/5。

  2. (P -> Q ; R)

    ?-      (list_memberd([1,2],X) -> X=2 ; X=4)。
    false。                                                % 错误
    ?- X=2, (list_memberd([1,2],X) -> X=2 ; X=4), X=2。
    X = 2。
    ?-      (list_memberd([1,2],X) -> X=2 ; X=4), X=2。
    false。                                                % 错误
    ?- X=4, (list_memberd([1,2],X) -> X=2 ; X=4), X=4。
    X = 4。
    ?-      (list_memberd([1,2],X) -> X=2 ; X=4), X=4。
    false。                                                % 错误
    

    正确性得分2/5。效率得分2/5。

  3. (P *-> Q ; R)

    ?-      (list_memberd([1,2],X) *-> X=2 ; X=4)。
    X = 2 或 false。                                        % 错误
    ?- X=2, (list_memberd([1,2],X) *-> X=2 ; X=4), X=2。
    X = 2 或 false。
    ?-      (list_memberd([1,2],X) *-> X=2 ; X=4), X=2。
    X = 2 或 false。
    ?- X=4, (list_memberd([1,2],X) *-> X=2 ; X=4), X=4。
    X = 4。
    ?-      (list_memberd([1,2],X) *-> X=2 ; X=4), X=4。
    false。                                                % 错误
    

    正确性得分3/5。效率得分1/5。


(初步)摘要:

  1. (P,Q ; non_P,R) 是正确的,但需要离散实现 non_P

  2. (P -> Q ; R) 当实例化不足时会失去声明性语义。

  3. (P *-> Q ; R)(P -> Q ; R)“更少”不完整,但仍有类似的问题。


幸运的是,我们有替代方案:使用逻辑单调控制结构if_/3

我们可以这样使用if_/3和可实现列表成员关系的谓词memberd_t/3

?-      if_(memberd_t(X,[1,2]), X=2, X=4).
X = 2 ; X = 4.
?- X=2, if_(memberd_t(X,[1,2]), X=2, X=4), X=2.
X = 2.
?-      if_(memberd_t(X,[1,2]), X=2, X=4), X=2.
X = 2 ; false.
?- X=4, if_(memberd_t(X,[1,2]), X=2, X=4), X=4.
X = 4.
?-      if_(memberd_t(X,[1,2]), X=2, X=4), X=4.
X = 4.

正确性得分 5/5。效率得分 4/5。


15

10
在Prolog中,基本上有三种不同的方式来表达类似于if-then-else的语句。为了比较它们,考虑一下char_class/2。对于a和b,类别应该是ab,对于所有其他项,类别应该是other。可以这样笨拙地编写这个语句:
char_class(a, ab).
char_class(b, ab).
char_class(X, other) :-
   dif(X, a),
   dif(X, b).

?- char_class(Ch, Class).
   Ch = a, Class = ab
;  Ch = b, Class = ab
;  Class = other,
   dif(Ch, a), dif(Ch, b).

为了更简洁地编写代码,需要使用if-then-else结构。Prolog内置了这样一个结构:
?- ( ( Ch = a ; Ch = b ) -> Class = ab ; Class = other ).
   Ch = a, Class = ab.

虽然这个答案是正确的,但它不完整。只给出了第一个答案( Ch = a ; Ch = b )。其他答案被切除了。确实不太关联。
一个更好的构造,通常称为“软切割”(不要相信名称,切割就是切割),可以得到稍微更好的结果(这是在YAP中)。
?- ( ( Ch = a ; Ch = b ) *-> Class = ab ; Class = other ).
   Ch = a, Class = ab
;  Ch = b, Class = ab.

另外,SICStus还有一个具有非常相似语义的if/3
?- if( ( Ch = a ; Ch = b ), Class = ab , Class = other ).
   Ch = a, Class = ab
;  Ch = b, Class = ab.

所以最后一个答案仍然被压制了。现在输入library(reif),适用于Scryer、SICStusYAPSWI。安装它并说:
?- use_module(library(reif)).

?- if_( ( Ch = a ; Ch = b ), Class = ab , Class = other ).
   Ch = a, Class = ab
;  Ch = b, Class = ab
;  Class = other,
   dif(Ch, a), dif(Ch, b).

请注意,所有的if_/3都会被编译成一个嵌套复杂的if-then-else语句。
char_class(Ch, Class) :-
   if_( ( Ch = a ; Ch = b ), Class = ab , Class = other ).

在YAP 6.3.4中扩展为:

char_class(A,B) :-
   (  A\=a ->
      (  A\=b ->
         B=other
      ;  A==b ->
         B=ab
      ;  A=b,
         B=ab
      ;  dif(A,b),
         B=other
      )
   ;  A==a ->
      B=ab
   ;  A=a,
      B=ab
   ;  dif(A,a),
      (  A\=b ->
         B=other
      ;  A==b ->
         B=ab
      ;  A=b,
         B=ab
      ;  dif(A,b),
         B=other
      )
   ).

4

最好的方法是使用所谓的cuts,这个符号是!

if_then_else(Condition, Action1, Action2) :- Condition, !, Action1.  
if_then_else(Condition, Action1, Action2) :- Action2.

上面是一个条件函数的基本结构。
举个例子,这里有一个“max”函数:
max(X,Y,X):-X>Y,!.  
max(X,Y,Y):-Y=<X.

我建议阅读更多有关"cuts"的文件资料,但是总体来说它们就像断点一样。 例如:如果第一个max函数返回一个true(真)值,第二个函数就不会被验证。
附注:我对Prolog相当新手,但这是我找到的资料。

不确定为什么这篇文章被投票否决了。在Prolog中,冷切片是处理条件的最强大的方式。 - gaurav.singharoy
5
“cold cuts”指的是冷切肉片,与Prolog有关。 - false
那个 max(X,Y,X):-X>Y,!. 在剪切之后应该设置第三个参数为steadfast - https://dev59.com/5lkT5IYBdhLWcg3wAK_w - brebs
@brebs 从我最初发布这篇帖子到现在已经过去了10年,我从未想过它会收到评论... 干杯 =D - skipper
@brebs 我从来没有想过这篇帖子会在原始发布后的10年里收到评论... 干杯 =D - undefined

3
(  A == B ->
     writeln("ok")
;
     writeln("nok")
),

else部分是必需的。

2
明确指定else部分肯定是个好主意,但并非必须的。 - false

3

Prolog程序实际上是一个带有“if”和“then”的大条件语句,它会打印“目标已达成”,并且还有一个“else”语句,它会打印“找不到解决方案”。A,B表示“A为真且B为真”,大多数Prolog系统将不会尝试满足“B”,如果“A”无法到达(即X=3,write('X is 3'),nl将在X=3时打印“X是3”,如果X=2,则什么也不会发生)。


2
你应该阅读Learn Prolog Now! 第10.2章 使用cut。这里提供了一个例子:
``` max(X,Y,Z) :- X =< Y,!, Y = Z. ```
即,
当且仅当 `X` 小于等于 `Y` 时,如果 `!` 为真(它总是为真),则 `Z` 等于 `Y`。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接