我是Prolog的新手并试图用Lists进行一些编程 我想这样做:
?- count_occurrences([a,b,c,a,b,c,d], X).
X = [[d, 1], [c, 2], [b, 2], [a, 2]].
这是我的代码,我知道它不完整,但我正在尝试:
count_occurrences([],[]).
count_occurrences([X|Y],A):-
occurrences([X|Y],X,N).
occurrences([],_,0).
occurrences([X|Y],X,N):- occurrences(Y,X,W), N is W + 1.
occurrences([X|Y],Z,N):- occurrences(Y,Z,N), X\=Z.
我的代码错了所以我需要一些点击或帮助PLZ ..
答案 0 :(得分:3)
请注意,到目前为止,所有提案都存在包含变量的列表的困难。想想这个案子:
?- count_occurrences([a,X], D).
应该有两个不同的答案。
X = a, D = [a-2] ;
dif(X, a), D = [a-1,X-1].
第一个答案表示:列表[a,a]
包含a
两次,因此D = [a-2]
。第二个答案涵盖与X
不同的所有字词a
,对于那些,我们有一次出现a
,另一次出现另一个字词。请注意,第二个答案包括无限可能的解决方案,包括X = b
或X = c
或您希望的任何其他解决方案。
如果实现无法产生这些答案,则实例化错误应该保护程序员免受进一步损害。一些事情:
count_occurrences(Xs, D) :-
( ground(Xs) -> true ; throw(error(instantiation_error,_)) ),
... .
理想情况下,Prolog谓词被定义为纯粹的关系like this one。但通常,纯粹的定义效率很低。
这是一个纯粹而高效的版本。从高效率的角度来看,它不会留下任何不必要的选择点。我把@dasblinkenlight的定义作为灵感来源。
理想情况下,此类定义使用某种形式的if-then-else。但是,传统的(;)/2
写了
( If_0 -> Then_0 ; Else_0 )
是一种固有的非单调结构。我将使用单调的对手
if_( If_1, Then_0, Else_0)
代替。主要区别在于条件。传统的控制结构依赖于If_0
的成功或失败,它破坏了所有的纯洁。如果你写( X = Y -> Then_0 ; Else_0 )
,变量X
和Y
是统一的,在那个时间点最终决定是Then_0
还是Else_0
。如果变量没有充分实例化,那该怎么办?好吧,那么我们运气不好,并坚持Then_0
仅获得一些随机结果。
将此与if_( If_1, Then_0, Else_0)
对比。在这里,第一个参数必须是一个目标,它将在其最后一个参数中描述Then_0
或Else_0
是否属实。如果目标尚未确定,则可以选择两者。
count_occurrences(Xs, D) :-
foldl(el_dict, Xs, [], D).
el_dict(K, [], [K-1]).
el_dict(K, [KV0|KVs0], [KV|KVs]) :-
KV0 = K0-V0,
if_( K = K0,
( KV = K-V1, V1 is V0+1, KVs0 = KVs ),
( KV = KV0, el_dict(K, KVs0, KVs ) ) ).
=(X, Y, R) :-
equal_truth(X, Y, R).
答案 1 :(得分:2)
如果您使用SWI-Prolog,您可以:
:- use_module(library(lambda)).
count_occurrences(L, R) :-
foldl(\X^Y^Z^(member([X,N], Y)
-> N1 is N+1,
select([X,N], Y, [X,N1], Z)
; Z = [[X,1] | Y]),
L, [], R).
答案 2 :(得分:1)
应该更容易解决问题的一件事是设计一个辅助谓词来增加计数。
想象一个谓词,它接受一对[SomeAtom,Count]
列表和一个需要递增计数的原子,并生成一个具有递增计数的列表,或者对于第一次出现的原子而言[SomeAtom,1]
。这个谓词很容易设计:
increment([], E, [[E,1]]).
increment([[H,C]|T], H, [[H,CplusOne]|T]) :-
CplusOne is C + 1.
increment([[H,C]|T], E, [[H,C]|R]) :-
H \= E,
increment(T, E, R).
当我们添加第一个匹配项时,第一个子句用作基本情况。当head元素与所需元素匹配时,第二个子句用作另一个基本情况。最后一种情况是当head元素与所需元素不匹配时的情况的递归调用。
有了这个谓词,写count_occ
变得非常简单:
count_occ([], []).
count_occ([H|T], R) :-
count_occ(T, Temp),
increment(Temp, H, R).
这是Prolog的普通递归谓词,带有一个简单的基本子句和一个处理尾部的递归调用,然后使用increment
来计算头部元素。列表。
答案 3 :(得分:1)
这是我使用bagof/3
和findall/3
的解决方案:
count_occurrences(List, Occ):-
findall([X,L], (bagof(true,member(X,List),Xs), length(Xs,L)), Occ).
示例
?- count_occurrences([a,b,c,b,e,d,a,b,a], Occ).
Occ = [[a, 3], [b, 3], [c, 1], [d, 1], [e, 1]].
工作原理
列表bagof(true,member(X,List),Xs)
的每个不同元素都满足 X
,其中Xs
是一个列表,其长度等于X
中List
的出现次数}}:
?- bagof(true,member(X,[a,b,c,b,e,d,a,b,a]),Xs).
X = a,
Xs = [true, true, true] ;
X = b,
Xs = [true, true, true] ;
X = c,
Xs = [true] ;
X = d,
Xs = [true] ;
X = e,
Xs = [true].
外部findall/3
收集元素X
以及代表解决方案的列表中关联列表Xs
的长度。
编辑我:得益于CapelliC和Boris的建议,原来的答案得到了改善。
编辑II :如果给定列表中有自由变量,则可以使用setof/3
代替findall/3
。 setof/3
的问题是,对于空列表,它将失败,因此必须引入特殊子句。
count_occurrences([],[]).
count_occurrences(List, Occ):-
setof([X,L], Xs^(bagof(a,member(X,List),Xs), length(Xs,L)), Occ).
答案 4 :(得分:1)
你已经得到了答案。 Prolog是一种通常提供多种“正确”方法来解决问题的语言。如果您在答案中坚持任何顺序,那么您的回答并不清楚。因此,忽略顺序,一种方法是:
这种方法的主要优点是它可以将您的问题解构为两个明确定义(和解决)的子问题。
第一个很简单:msort(List, Sorted)
第二个涉及更多,但如果你希望谓词只能以一种方式工作,那么仍然是直接的,即List - >编码。一种可能性(非常明确):
list_to_rle([], []).
list_to_rle([X|Xs], RLE) :-
list_to_rle_1(Xs, [[X, 1]], RLE).
list_to_rle_1([], RLE, RLE).
list_to_rle_1([X|Xs], [[Y, N]|Rest], RLE) :-
( dif(X, Y)
-> list_to_rle_1(Xs, [[X, 1],[Y, N]|Rest], RLE)
; succ(N, N1),
list_to_rle_1(Xs, [[X, N1]|Rest], RLE)
).
所以现在,从顶层:
?- msort([a,b,c,a,b,c,d], Sorted), list_to_rle(Sorted, RLE).
Sorted = [a, a, b, b, c, c, d],
RLE = [[d, 1], [c, 2], [b, 2], [a, 2]].
另一方面,在X-N
中选择“对”几乎总是更好,而不是像[X, N]
中那样精确地使用两个元素的列表。此外,如果您想要正确,您应该保持列表中元素的原始顺序。来自this answer:
rle([], []).
rle([First|Rest],Encoded):-
rle_1(Rest, First, 1, Encoded).
rle_1([], Last, N, [Last-N]).
rle_1([H|T], Prev, N, Encoded) :-
( dif(H, Prev)
-> Encoded = [Prev-N|Rest],
rle_1(T, H, 1, Rest)
; succ(N, N1),
rle_1(T, H, N1, Encoded)
).
为什么会更好?
我们在代码
我们在报告的解决方案中摆脱了混乱
我们摆脱了大量不必要的嵌套术语:将.(a, .(1, []))
与-(a, 1)
我们让程序的目的更清晰(这是在Prolog中表示对的传统方式)
从顶层:
?- msort([a,b,c,a,b,c,d], Sorted), rle(Sorted, RLE).
Sorted = [a, a, b, b, c, c, d],
RLE = [a-2, b-2, c-2, d-1].
所提出的游程编码器在其定义中非常明确,当然它的优点和缺点。请参阅this answer了解更为简洁的方法。
答案 5 :(得分:1)
精炼joel76 answer:
count_occurrences(L, R) :-
foldl(\X^Y^Z^(select([X,N], Y, [X,N1], Z)
-> N1 is N+1
; Z = [[X,1] | Y]),
L, [], R).