从Prolog谓词获得多个答案

时间:2018-11-29 00:12:26

标签: prolog prolog-setof

假设我有一个谓词有时会给我多个输出。像这样-

foo(Number, Out) :- Number < 10, Out = less_than_ten.
foo(Number, Out) :- Number > 0, Out = more_than_zero.

如何记住Out在另一个谓词中给出的foo的所有值,并牢记它有时可以给出一个,有时也可以给出多个(例如,在列表中)?

编辑-不太确定我已经回答了我的问题,所以我会更具体。以上述谓词为例,我可以运行查询foo(5, Out).,这将满足这两个规则,因此,如果我在SWI-prolog中运行它,我将得到-

?- foo(5, Out).
Out = less_than_ten

然后我可以输入一个分号以使序言回溯并寻找其他解决方案-

?- foo(5, Out).
Out = less_than_ten ;
Out = more_than_zero.

因此,如果我在另一个内部执行此谓词,给定Number = 5,如何获得Out的所有有效值?

3 个答案:

答案 0 :(得分:3)

如果仅考虑整数,则可以选择使用CLP(FD)。然后您的谓词foo可能看起来像这样:

:- use_module(library(clpfd)).

foo(Nums) :-
   Nums ins 1..9.     % the numbers in the list Nums are 0 < X < 10

您可以使用此谓词来测试数字列表是否在所需范围内:

   ?- foo([1,2,3,4,5]).
yes
   ?- foo([0,1,2,3,4,5]).
no
   ?- foo([1,2,3,4,5,10]).
no

如果要使用它生成该范围内的整数列表,请确保Nums是一个列表,以避免实例化错误。您可以在查询中添加一个目标长度/ 2作为前缀:

   ?- length(Nums,_), foo(Nums).
Nums = [] ? ;          % <- no number
Nums = [_A],           % <- one number
_A in 1..9 ? ;
Nums = [_A,_B],        % <- two numbers
_A in 1..9,
_B in 1..9 ? ;
Nums = [_A,_B,_C],     % <- three numbers
_A in 1..9,
_B in 1..9,
_C in 1..9 ?
.
.
.

这些答案包括剩余目标(有关详细信息,请参见CLP(FD) documentation)。如果要查看实际数字,则必须添加目标以标记列表:

   ?- length(Nums,_), foo(Nums), label(Nums).
Nums = [] ? ;
Nums = [1] ? ;
Nums = [2] ? ;
Nums = [3] ? ;
.
.
.
Nums = [1,1] ? ;
Nums = [1,2] ? ;
Nums = [1,3] ? ;
.
.
.
Nums = [9,9] ? ;
Nums = [1,1,1] ? ;
Nums = [1,1,2] ? ;
.
.
.

答案 1 :(得分:0)

首先,谓词可以成功或失败。

从技术上讲,并在序言standard

中进行了注明

在Prolog中,您认为调用是执行。

  

执行是一系列试图满足目标的激活。

在Prolog中,您认为收益的意思是

  

如果BP激活成功,则更换当前的激活器   激活器true对CCG的激活,其激活描述于(7.8.1   .l)。

因此,通过将状态压入和弹出堆栈来传递值。

为简单起见,我将改用更通用的编程术语(调用,传递,参数),即使它们在技术上不是用于描述Prolog的正确术语。

要从foo调用bar,以便bar可以将值传递给foo,就像这样:

bar :-
    foo(Passed_to_foo).

foo(Received_from_bar) :-
    % Do something with Received_from_bar.

要扩展前一个并将一个值从foo传递回bar,将像这样:

bar :-
    foo(Passed_to_foo,Received_from_foo).

foo(Received_from_bar,Passed_to_bar) :-
     % Do something with Received_from_bar
     % Generate/Compute Passed_to_bar

如果您不知道该参数是否将不包含任何项目,一个项目或一个或多个项目,那么您在考虑列表时是正确的,但是列表也可以同时执行全部三个操作。

要不传递任何值作为列表,只需传递一个空列表

[]

要在列表中传递一个值,只需将其放在列表中并传递列表

[first_value]

要传递多个值,只需将它们放在列表中并传递列表

[first_value, second_value, third_value, ... ]

由于值将始终位于列表中,因此只需使用良好的旧递归来处理列表中的项目即可。

基本情况

foo([],Result).

递归案例

foo([H|T],Result) :-
    % Do something with head of list `H`
    % Pass tail of list `T` back to foo for processing
    foo(T,Result).

答案 2 :(得分:0)

刚刚找到了可以回答这个问题的谓词。 Prolog具有内置谓词bagof(Template, Goal, Bag),它将Template谓词的Goal自变量与Bag自变量统一起来(不确定那是正确的Prolog语言!)。因此,在问题的示例中,使用bagof(Out, foo(5, Out), Outputs).,这将与Out合并Outputs的解决方案列表。在SWI-prolog中运行查询:

4 ?- bagof(Out, foo(5, Out), Outputs).
Outputs = [less_than_ten, more_than_zero].

A useful guide for different ways to find all solutions to a goal.