如何在列表中添加和比较成员



我正在尝试编写一个谓词is_multi(M),定义为:

  • M的每个元素都具有X / N的形式,其中X是原子,N是大于0的整数
  • M不包含两个原子相同的元素,因为

    is_multi([]).
    is_multi([a / 2, b / 2]).
    

    都很满意,但是

    is_multi([a, b/2]).
    is_multi([a/0, b/2]).
    is_multi([a/2, 2/4])
    is_multi([a/2, b/3, a/2])
    is_multi([a/3, b/-4, c/1])
    

    不是。

以下是我迄今为止所写的内容:

is_multi(M) :- M = [].
is_multi(M) :-
    M = [Head|Tail],
    Head = X/N,
    integer(N),
    N > 0,
    is_multi(Tail).

但如果两个元素是同一个原子,它就不会比较。例如,不满足is_multi([a/2, a/3])。我被这个问题困扰了一天;有人能给我一些提示吗?

首先,您可以通过将一些统一从主体移动到头部来大大简化代码。

is_multi([]).
is_multi([X/N|Tail]) :-
  integer(N), N > 0,
  is_multi(Tail).

清理它会发现你在这里没有做的一件事,在你的规范中,就是检查X是否是原子。通过将atom(X)添加到主体来修复。

好的,这样可以处理基本形式,但不能确保原子不会重复。最简单的方法是将其分为两个检查,一个检查每个项目是否格式正确,另一个检查列表是否格式正确。事实上,我倾向于将maplist/2与检查单个元素的谓词一起使用。但你真正要做的就是这样的事情:

is_valid([]).
is_valid([X/_|T]) :- is_valid(T), + memberchk(X/_, T).

这只是说空列表是有效的,如果尾部有效,那么如果尾部中没有出现X,那么列表就是有效的。

如果这就是你想要的,那就别在那里看书了。如果你想重构,我会这样做:

well_formed(X/N) :- atom(X), integer(N), N > 0.
no_repeating_numerators([]).
no_repeating_numerators([X/_|T]) :- no_repeating_numerators(T), + memberchk(X/_, T).
is_multi(L) :- maplist(well_formed, L), no_repeating_numerators(L).

为了完成Daniel的启发性回答(我的回答是+1),我想展示如何通过一些库谓词来解决您的任务:

is_multi(L) :-
    forall(select(E, L, R),
           (E = A/N, atom(A), integer(N), N > 0, +memberchk(A/_, R))).

最新更新