检查列表中的元素是否相等(Prolog)

3
我正在尝试检查一个列表中的所有子列表是否等于列表的长度。

例如,如果我有[[1,2],[3,4]],那么是正确的,因为我有2个由2个元素组成的列表。

否则,如果我有

[[1],[2,3]]是错误的,因为有2个列表,但并不是所有的列表都有2个元素

[[1,2],[2,3],[3,4]]是错误的,因为我有2个列表,而所有列表都有2个元素,而不是两个

.

我编写了这两个函数:

count([],0).
count([_H|T],N):-count(T,N1),N is N1+1 .

ma([],0).
ma([H|T],N):- count(H,M1),ma(T,N1), M1 is N1.

我对列表中“count”元素进行了计数,并返回了列表中的元素数量N。

由于“count”被执行到0并返回2,所以“ma”函数无法正常工作。经过执行ma函数后到1步骤后,直接生成M1为N1,显然会返回false。我希望在程序结束时使M1等于N1(就像另一种编程语言中一样),但我认为这不是正确的形式。

编辑:

Daniel建议使用:

ma([H],   N) :- length(H, N).
ma([H|T], N) :- length(H, N), ma(T, N).

然而,当一个列表有3个子列表,每个子列表都有2个元素时,结果会是2。实际上,结果应该为false(错误),因为列表的数量N必须等于所有子列表中元素的数量N。

我将自己完成此任务,而不使用Prolog的内置谓词。


我的名字是丹尼尔,不是大卫。 - Daniel Lyons
2个回答

1
你的 count/2 类似于内置的 length/2,除了内置的有更多的实例化模式(尝试使用 length(X, Y))。建议使用 length/2
你是对的,你的 ma/2 谓词没有帮助,因为 0 不是子列表的长度。基本上,你在这里选择了错误的基本情况;你的基本情况应该是一个恰好有一个项目的列表:
ma_1([H],   N) :- length(H, N).
ma_1([H|T], N) :- length(H, N), ma(T, N).

你需要将此内容包装在某个东西中,以确保其长度与外部列表的长度相匹配:
ma(L, N) :- length(L, N), ma_1(L, N).

请注意,不需要获取单独的变量并断言它们的相等性(您与N和N1的交互)。如果N没有正确的值,Prolog将会失败,这就是你想要的。(另外,请勿使用is进行统一。 is的目的是将右侧的算术表达式简化为一个值,并将其分配给左侧的变量,例如X is 2 + 3*4。)
另一种方法是编写您实际请求的逻辑形式并代之以此。这个请求的逻辑形式是“ma(L,N)成立,如果N是L的长度,并且对于L的所有项目X,它们的长度也是N”。它看起来像这样:
ma(L, N) :- 
    length(L, N), 
    forall(member(X, L), 
           length(X, N)).

这种方法的优点在于不会留下多余的选择点,虽然担心这一点通常是过早优化。
另一种方法是使用maplist/N,它的优点是可以返回带有变量的列表。不幸的是,length/2的参数顺序是错误的,所以你不能只写maplist(length(2), L)。不过,你可以创建一个flip/3谓词来翻转参数。
flip(P, Y, X) :- call(P, X, Y).

ma(L, N) :- length(L, N), maplist(flip(length, N), L).

或者,您可以导入library(yall)并使用其lambda表达式:

ma(L, N) :- length(L, N), maplist({N}/[X]>>length(X, N), L).

这两种方法都可以实现以下解决方案:
?- ma(X, N).
X = [],
N = 0 ;

X = [[_1976]],
N = 1 ;

X = [[_1982, _1988], [_1994, _2000]],
N = 2 ;

X = [[_1988, _1994, _2000], [_2006, _2012, _2018], [_2024, _2030, _2036]],
N = 3 
...

使用maplist代替forall也可以处理ma(L,2) - false
@false 我曾经考虑过这个问题,但我认为 length/2maplist(length(2), L) 中的参数顺序是错误的。还有其他方法吗? - Daniel Lyons
@DanielLyons:是的,您可以使用lambda表达式。但是很遗憾,据我所知,没有“flip”谓词。 - Willem Van Onsem
嗨,丹尼尔,谢谢你的回答。你使用了我没有用过的更复杂的谓词。我知道length/2是内置的,但我必须自己做,因为我的教授不希望我们使用内置的谓词:S 我尝试了你的ma/2,但它与我的东西不兼容,因为当你调用ma([1,2],[2,3],[3,4],X)时会给出2...这是错误的,因为应该失败...我将编辑我的问题。 - theantomc
@theantomc 我已经添加了一个小包装器来防止这个问题。 - Daniel Lyons

1

这里是一个没有内置谓词的非常基本的解决方案:

count_elements([],N,N).
count_elements([_|T],N,N0):-
    N1 is N+1,
    count_elements(T,N1,N0).

count_length_sub([],_).
count_length_sub([H|T],N):-
    count_elements(H,0,N),
    count_length_sub(T,N).

solve(L):-
    count_elements(L,0,NO),
    count_length_sub(L,NO).

?- solve([[1,2],[3,4]]).
true.

?- solve([[1,2],[2,3],[3,4]]).
false.

非常感谢!我也理解了这个哲学! - theantomc
1
使用succ/2而不是... is X+1更好,因为它具有更多的实例化。 - Daniel Lyons

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