Prolog - 如何检查谓词是否多次成功

mar*_*ian 5 prolog

我有一个这样的事实数据库:

li(a,2).
li(b,3).
li(b,1).
li(c,2).
li(d,1).
li(d,1).
Run Code Online (Sandbox Code Playgroud)

如果它存在多个事实li(Let,_),我需要编写一个更成功的谓词(+ Let).

例如,更多(b)和更多(d)的查询将成功,但更多(a)和更多(c)将不会.我的想法是检查li(Let,_)是否不止一次成功,但我不知道该怎么做.

Wil*_*ess 6

试试findall/3:

findall(X, li(d,X), L), length(L,N), N>1.
Run Code Online (Sandbox Code Playgroud)

抽象d出来并制作谓词是微不足道的.对?:)


如果您不想使用任何谓词findall,您可以更改您的知识表示 - 将其降低到一个级别,可以这么说:

my_knowledge(li, [a-2,b-3,b-1,c-2,d-1,d-1]).
Run Code Online (Sandbox Code Playgroud)

然后你可以使用SWI Prolog的谓词select/3来处理它:

select_knowledge(kn, key, R):-
  my_knowledge(kn,L),
  select_key(L,key,R).

select_key(L,K,R):-
  select(K-X,L,L1) -> R=[X|R1], select_key(L1,K,R1)
  ; R = [].
Run Code Online (Sandbox Code Playgroud)

您可以将最后一个谓词重写为列表上的基本递归,然后在获得前N个结果后将其调整为停止.

  • @markusian然后你运气不好.计算谓词成功的次数不是逻辑,它高于逻辑 - 即它不是证明某些东西,而是关于证明某事物的机制的运作.至少你必须使用`assert` /`retract`或`flag`等.`readall`更容易使用. (2认同)

fal*_*lse 5

more_than_once(Goal) :-
   \+ \+ call_nth(Goal,2).
Run Code Online (Sandbox Code Playgroud)

与此答案中call_nth/2定义的一样.

与其他解决方案相比,该解决方案的最大优势在于,即使存在非常大的答案序列,它也能够快速成功.事实上,它甚至可以成功获得无限的答案:

?- more_than_once(repeat).
true.

?- more_than_once(between(1,100000,_)).
true.

(实现call_nth/2使用一些非标准的,低级别的SWI内置.可以避免这种情况,但更令人头痛.)