Prolog统计谓词为真的次数

35

我想要计算一个自定义谓词为真的次数。 例如,我有以下代码:

is_man(john).
is_man(alex).
?:-is_man(X).
< p >< code >X< /code >将返回 < code >john< /code >,然后如果我按分号键,它还会返回 < code >alex< /code >,然后是 < code >false< /code >。< /p >

我想要构建类似于:< /p >

count(is_man(X), Count).

并且这个返回

Count = 2

我该怎么做?


重复:https://dev59.com/qW025IYBdhLWcg3wkG17#5930420 - Kaarel
3个回答

41

在SWI-Prolog中:

aggregate_all(count, is_man(X), Count).

是否可以创建一个不同的计数? 比如,如果你有两次相同的名字,不要增加计数器。 - Shevliaskovic
2
@Shevliaskovic:简短的回答是可以的。aggregate_all/3使用findall/3并通过回溯计算找到的解决方案,包括相同的解决方案。但是,aggregate_all/4aggregate/4允许计数忽略重复项的方式。这可能值得您提出一个单独的问题。 - hardmath

13
对于一个符合ISO标准的Prolog解决方案,您可以使用findall/3生成所有解的列表,然后将Count设置为所得到的列表长度。尝试按照您提出的方法将其包装成用户定义谓词count/2可能有些棘手,因为我们需要以一种方式形成findall/3的第一个参数,以便考虑要作为count/2第一个参数传递的目标中的任何自由(未绑定)变量。
许多Prolog提供“计数器”或其他形式的可变全局值,这是一种非标准扩展,可以与失败驱动的“循环”一起使用以进行相同的计数。稍微麻烦一些,但遵循Prolog标准的字面意思,可以使用assertretract来创建自己的“计数器”,通过调整动态事实来完成。
下面是后一种方法的示例。使其“多线程安全”需要额外的逻辑。
count(Goal,_) :-
    setGoalCount(0),
    call(Goal),
    incGoalCount(1),
    fail.              /* or false in some Prologs */
count(_,Count) :-
    getGoalCount(Count).

setGoalCount(_) :-
    retract(getGoalCount(_)),
    fail.
setGoalCount(X) :-
    assert(getGoalCount(X)).

incGoalCount(Y) :-
    retract(getGoalCount(X)),
    !,
    Z is X + Y,
    assert(getGoalCount(Z)).

3
不是关于“多线程安全”的问题。简单的嵌套无法正常工作:count(count((true;true),_),C) 应该得到 1,但你得到了 3。此外,ISO 标准没有 assert/1,而是使用 asserta/1assertz/1 - false
2
嗨,是的,这让我想起了我们最近在comp.lang.prolog线程中讨论的话题!如果不需要findall/3或“全局变量”解决方案,那么这也是sys_make_surrogate/1和setup_call_cleanup/3的商业案例。 - user502187

10

count(P,Count) :-
        findall(1,P,L),
        length(L,Count).

我找不到包含函数 length()findall() 的库。 - Tarek
@Tarek 两者都是内置谓词,不属于任何特殊库。您可以直接使用它们,无需导入任何库。 - ThomasH

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