如何检查某个变量是否在谓词中返回某些内容

时间:2017-12-06 04:57:32

标签: prolog

让我们假设我有如下事实:

airport(nyc,'newyork').

如果用户输入不存在的机场,我想要显示消息。

我的尝试:

isAirport(Air)  :-
      airport(Air,T),
     (var(T) -> true 
      ; 
       write('Airport not found'),
       fail
      ).

然而,这似乎不起作用。

2 个答案:

答案 0 :(得分:3)

首先让我们看看如果首先查询一个连词(运算符)会发生什么:

?- airport(nyc, _), write('found!').
found!
true.

?- airport(abc, _), write('found!').
false.

这意味着isAirport(abc)在尝试airport(abc,_)后没有评估其余谓词时直接失败。因此,在许多情况下,您可以在没有明确的if-then-else结构的情况下使用,只需编写

形式的内容
predicate(X) :-
  first_condition(X),
  second_condition(X).

只有X满足两个条件才会成功。

如果你真的想要创建一些用户界面,这有点棘手,因为I / O本质上是非逻辑的,特别是当涉及回溯时。我们通常调用一个程序,其行为类似于我们对纯逻辑公式的期望,当它包含非逻辑结构(如I / O或切割运算符!)时称为不纯。

不幸的是,if-then-else结构(->;)和否定(\+)是通过cut实现的,因此也是不纯的。幸运的是,大多数时候人们想要一个有条件的,纯粹的分离就足够了:

case(1,a).
case(2,b).

我们从Prolog的执行机制中自动分支:

?- case(X,Y).
X = 1,
Y = a ;
X = 2,
Y = b.

但有时我们真的想做一些需要不纯的结构的东西,比如用户输入。那么保持程序良好逻辑属性的最简单方法是将任务分为纯粹和不纯的任务:

main :-
    uinput(Data),
    pure_predicate(Data, Result),
    write(Result).

完成所有不纯的部分后,Data与我们想要的用户数据统一。我们来看看uinput/1

的实现
uinput(data(Airport,D-M-Y)) :-
    format('~nAirport? '),
    read(Airport),
    ( ground(Airport), airport(Airport, _) )
    ->
        (
            format('~nDay? '),
            read(D),
            format('~nMonth? '),
            read(M),
            format('~nYear? '),
            read(Y),
            ( ground(D-M-Y), isDate(D-M-Y) )
        ->
        true
        ;
        throw(failure('unknown date'))
        )
    ;
    throw(failure('unknown airport'))
    .

我们连续从输入中读取术语,如果我们无法处理它,则抛出异常。对于if-then-else结构,我们需要特别小心。如果我们比较两个查询:

?- between(1,3,X), write(X).
1
X = 1 ;
2
X = 2 ;
3
X = 3.

?- between(1,3,X) -> write(X); false.
1
X = 1.

你可以看到if-then-else正在失去解决方案。这意味着我们需要确保我们的条件是确定性的。要求用户输入术语是一个好主意,因为没有变量,只有一个解决方案术语。但是,对其中一个数据谓词airport/1isDate/1的调用可能会多次生成相同的术语,或者根本不会终止。在这种特殊情况下,我们只需要确保每个机场都有一个唯一的快捷方式名称,我们也可以生成日期而不重复:

airport(nyc, 'New York City').
airport(wdc, 'Washington DC').

isDate(X-Y-Z) :-
    between(1,31,X),
    between(1,12,Y),
    between(1970,2100,Z).

实施uinput的另一个诀窍是,当我们验证了所有内容后,我们才能成功true。现在唯一的影响是Data用用户输入的任何内容进行实例化。

如果我们给出实际实现的虚拟实现,我们已经可以尝试实现oursevles:

pure_predicate(_Data, Result) :-
    % here goes the actual stuff
    Result='we have found something awesome'.

在提示符下,我们可以毫无困难地使用纯谓词:

?- pure_predicate(someinputdata,Y).
Y = 'we have computed something awesome'.

另一方面,我们也可以使用完整谓词如下:

?- main(_).

Airport? wdc.

Day? |: 1.

Month? |: 2.

Year? |: 2000.
we have found something awesome
true.

由于我们使用read,我们必须输入prolog术语并以点.结束,但一切都按预期工作。

如果用户输入失败,我们会得到:

?- main(_).

Airport? bla(X).

ERROR: Unhandled exception: failure('unknown airport')

请注意,我们只是遇到了这个问题,实际上很早就失败了,并在这种情况下给用户留言。对于实际计算,这是完全不必要的。

答案 1 :(得分:1)

在下面的代码中,假设如果在数据库中找不到机场,T将保持不受约束:

airport(Air, T)

实际上发生的事情是,机场(Air,T)的呼叫将使机场(空中)立即失效,并且您的var(T)和其他部分将不会被执行。

请尝试使用此代码:

isAirport(Air) :-
    airport(Air, _T), ! ; write('Airport not found'), fail.