Prolog 计数出现次数序言
我是Prolog新手,正在尝试使用列表进行编程Prolog 计数出现次数序言,prolog,Prolog,我是Prolog新手,正在尝试使用列表进行编程 我想这样做: ?- 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). occurre
我想这样做:
?- 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。一件应该使解决问题更容易的事情是设计一个帮助谓词来增加计数 想象一个谓词,它接受一个成对的列表
[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\u occ
变得非常简单:
count_occ([], []).
count_occ([H|T], R) :-
count_occ(T, Temp),
increment(Temp, H, R).
这是Prolog的常规递归谓词,带有一个普通的base子句和一个递归调用,该调用处理尾部,然后使用increment
来解释列表的head元素
这是我使用
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,成员(X,List),Xs)
满足列表X
的每个不同元素,其中Xs
是一个长度等于X
在列表中出现次数的列表:
?- 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
编辑一:多亏了Capelical和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).
如果使用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).
你已经得到了答案。Prolog是一种语言,它通常提供多种“正确”的方法来处理问题。从你的回答中不清楚你是否坚持回答中的任何顺序。因此,忽略顺序,一种方法是:
使用稳定排序(不删除重复项的排序)对列表进行排序
对排序列表应用运行长度编码
这种方法的主要优点是,它将您的问题分解为两个定义良好(并已解决)的子问题
第一个很简单:msort(列表,排序)
第二个更复杂,但如果希望谓词只以一种方式工作,即列表-->编码,则仍然是直接的。一种可能性(非常明确):
所以现在,从最高层:
?- 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]].
?- 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].
另一方面,最好选择“配对”,如X-N
,而不是像[X,N]
那样只列出两个元素。此外,如果希望正确,您应该保持列表中元素的原始顺序。发件人:
为什么更好
- 我们在代码中去掉了4对不必要的括号
- 我们在报告的解决方案中消除了混乱
- 我们去掉了大量不必要的嵌套术语:将
(a,.(1,[])
与-(a,1)
- 我们让读者更清楚地了解了程序的意图(这是在Prolog中表示成对的传统方法)
从最高层:
?- 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]].
?- 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].
所提出的游程编码器的定义非常明确,这当然有其优点和缺点。请参阅,以获取更简洁的操作方法。精炼joel76:
请注意,到目前为止,所有提案都难以处理同样包含变量的列表。想想这个例子:
?- count_occurrences([a,X], D).
应该有两种不同的答案
X = a, D = [a-2] ;
dif(X, a), D = [a-1,X-1].
第一个答案的意思是:列表[a,a]
包含a
两次,因此D=[a-2]
。第二个答案涵盖了与a
不同的所有术语X
,对于这些术语,我们有一次出现a
,另一次出现该术语。请注意,第二个答案包括无限多可能的解决方案,包括X=b
或X=c
或您希望的任何其他解决方案
如果一个实现不能产生这些答案,实例化错误应该可以保护程序员免受进一步的损害。一些事情:
count_occurrences(Xs, D) :-
( ground(Xs) -> true ; throw(error(instantiation_error,_)) ),
... .
理想情况下,Prolog谓词定义为纯关系。但通常,纯粹的定义效率很低
这是一个纯粹而高效的版本。有效的意义在于它不会留下任何不必要的选择点。我把@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\u 0
,就会得到一些随机结果
将此与if\u1(if\u1,Then\u0,Else\u0)
进行对比。这里,第一个参数必须是某个目标,它将在最后一个参数中描述Then\u 0
或Else\u 0
是否是这种情况。目标应该是什么
if_( If_1, 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).