Prolog|findall,带有另一个findall的结果列表



如何通过特定条件获取值,然后用这些选定的元素从其他事实序列中获取值?

我有这个代码

%code, date, amount
values1('AAA', date(02, 03, 2020), 1000).
values1('AAA', date(31, 03, 2020), 2000).
values1('BBB', date(02, 04, 2020), 1350).
values1('BBB', date(15, 04, 2020), 1500).
values1('CCC', date(23, 05, 2020), 5500).
values1('CCC', date(01, 05, 2020), 750).
values1('DDD', date(06, 05, 2020), 3560).
values1('AAA', date(18, 06, 2020), 4600).
values1('CCC', date(27, 07, 2020), 1200).
%code, percent
values2('AAA', '0.2').
values2('BBB', '0.25').
values2('CCC', '0.55').
values2('DDD', '0.98').
predicate1(Code, Percent) :- 
findall(Code, (values1(Code, _, Value), Value > 1000), Code),
findall(Percent, values2(Code, Percent), Percent).

例如,在这种情况下,它的想法是获取金额大于1000的代码,当选择时,从值2中获取百分比,但百分比列表返回为空。为什么会这样?

这是返回的

predicate1(Code, Percent).
Code = ['AAA', 'BBB', 'BBB', 'CCC', 'DDD', 'AAA', 'CCC'],
Percent = []

它可能是由代码列表中的元素数量和值的事实数量之间的差异2可能吗?

您有一个打字错误和一些逻辑错误。

Code的值进入Code:CodeList的列表中。变量Code位于第一个和第三个位置。使用此:

findall(Code, (values1(Code, _, Value), Value > 1000), CodeList).

运行它:

?- findall(Code, (values1(Code, _, Value), Value > 1000), CodeList).
CodeList = ['AAA', 'BBB', 'BBB', 'CCC', 'DDD', 'AAA', 'CCC'].

通过使用谓词setof/3来删除重复的元素。

然而,setof/3要求您指示("存在量化"用插入符号表示:Value^Date^(在内部目标之外不可见的变量,否则ValueDate的可能值的回溯将发生在setof/3:之外

?- setof(Code, Value^Date^(values1(Code, Date, Value), Value > 1000), CodeList).
CodeList = ['AAA', 'BBB', 'CCC', 'DDD'].

现在你只需要";加入";任何";代码";用";百分比"。。。setof/3:内部

?- setof([Code,Percent], 
Value^Date^(values1(Code, Date, Value),
Value > 1000,
values2(Code,Percent)),
Set).
Set = [['AAA', '0.2'], ['BBB', '0.25'], ['CCC', '0.55'], ['DDD', '0.98']].

您可以将其打包为谓词,该谓词回溯setof/3结果:

gimme(Code, Percent) :-
setof([Code,Percent], 
Value^Date^(values1(Code, Date, Value),
Value > 1000,
values2(Code,Percent)),
Set),
member([Code,Percent],Set).

请注意,在setof/3内部和对member/2的调用中重用CodePercent的变量实际上是可以的:它们是而不是相同的变量。

因此:

?- gimme(Code,Percent).
Code = 'AAA',
Percent = '0.2' ;
Code = 'BBB',
Percent = '0.25' ;
Code = 'CCC',
Percent = '0.55' ;
Code = 'DDD',
Percent = '0.98'.
predicate1(Code,Percent)
:-
values1(Code,_,Amount) ,
Amount > 1000 ,
values2(Code,Percent)
.

来自顶层的示例用法:

?- predicate1(Code,Percent).
Code = 'AAA',
Percent = '0.2' ;
Code = 'BBB',
Percent = '0.25' ;
Code = 'BBB',
Percent = '0.25' ;
Code = 'CCC',
Percent = '0.55' ;
Code = 'DDD',
Percent = '0.98' ;
Code = 'AAA',
Percent = '0.2' ;
Code = 'CCC',
Percent = '0.55'.

其他答案为您提供回溯的解决方案。如果你真的想要列表(见下面的注释(,你需要一个可以关联";CCD_ 17元素的列表";以及";CCD_ 18元素的列表";使得每个CCD_ 19-CCD_。

SWI Prolog对此有映射列表/N:

predicate1(Codes, Percents) :-
findall(Code, (values1(Code, _, Value), Value > 1000), Codes),
maplist(values2, Codes, Percents).

这将为您提供代码和相应百分比的等长列表:

?- predicate1(Codes, Percents).
Codes = ['AAA', 'BBB', 'BBB', 'CCC', 'DDD', 'AAA', 'CCC'],
Percents = ['0.2', '0.25', '0.25', '0.55', '0.98', '0.2', '0.55'].

注意:Prolog初学者喜欢将解决方案收集到列表中。这通常不是解决Prolog中问题的最佳方法。回溯可以更自然、更简单、更高效。我建议你尽量克制把所有东西都收集到列表中的冲动,除非真的需要。

编辑:关于样式,如果我确实使用findall/3setof/3bagof/3,我更喜欢编写一个辅助谓词来隐藏与我要收集的数据无关的所有变量:

code_above(Code, Limit) :-
values1(Code, _, Value),
Value > Limit.

这样一来,findallsetof调用看起来是一样的,因此可以很容易地在它们之间切换。它们(在我看来(也比setof所要求的Value^Date^体操可读性强得多,正如David Tonhofer的回答所示:

predicate1(Codes, Percents) :- 
findall(Code, code_above(Code, 1000), Codes),
maplist(values2, Codes, Percents).
predicate2(Codes, Percents) :- 
setof(Code, code_above(Code, 1000), Codes),
maplist(values2, Codes, Percents).

有了这个,你可以免费消除重复:

?- predicate1(Codes, Percents).
Codes = ['AAA', 'BBB', 'BBB', 'CCC', 'DDD', 'AAA', 'CCC'],
Percents = ['0.2', '0.25', '0.25', '0.55', '0.98', '0.2', '0.55'].
?- predicate2(Codes, Percents).
Codes = ['AAA', 'BBB', 'CCC', 'DDD'],
Percents = ['0.2', '0.25', '0.55', '0.98'].

最新更新