我想解决一个问题,即我有一个Prolog的元素列表。如果任何元素频率大于N
,则返回false。我的期望如下。
?- frequency([1,2,2,2,5],3).
true.
?- frequency([1,2,2,2,2,5],3).
false.
我有一个获取特定元素频率的代码。对这个问题有所了解。
count(_, [], 0) :-
!.
count(X, [X|T], N) :-
count(X, T, N2),
N is N2 + 1.
count(X, [Y|T], N) :-
X \= Y,
count(X, T, N).
答案 0 :(得分:4)
使用clpfd!
:- use_module(library(clpfd)).
如果我们建立辅助谓词list_counts/2
,我们可以像这样定义frequency/2
:
frequency(Es, M) :- list_counts(Es, Xss), maplist(arg(2), Xss, Zs), maplist(#>=(M), Zs).
示例查询:
?- frequency([1,2,2,2,5], 3).
true.
?- frequency([1,2,2,2,2,5], 3).
false.
感谢clpfd我们可以询问相当普遍的查询 - 并获得逻辑上合理的答案!
?- frequency([A,B,C], 2).
A=B , dif(B,C)
; A=C , dif(B,C)
; dif(A,C), B=C
; dif(A,B), dif(A,C), dif(B,C).
答案 1 :(得分:3)
你可以先考虑一下最便宜的" (在写作方面)你可以攻击这样的问题。我通常试图弄清楚如何使用标准命令行工具。例如,要在名为foo
的文本文件中查找所有行的出现次数,可以写一个(在Bash中):
sort foo | uniq --count
您可以阅读手册,但这里有一个示例:
$ cat foo
a
b
b
b
c
d
d
a
b
d
c
$ sort foo | uniq --count
2 a
4 b
2 c
3 d
现在,有一种方式可以询问"是否有任何数量超过3的线?"是这样使用awk
:
sort foo | uniq --count | awk '{ if ($1 > 3) exit(1) }'
(我相信也有更聪明的方法。)
使用上述文件,您将获得:
$ sort foo | uniq --count | awk '{ if ($1 > 3) exit(1) }'
$ echo $?
1
$ sort foo | uniq --count | awk '{ if ($1 > 4) exit(1) }'
$ echo $?
0
好的,那对Prolog有什么帮助?好吧,一种简单的方法来模拟这样的管道:
foo | bar | baz # etc
是在Prolog中写一个像这样的连词:
foo(In, X0), bar(X0, X1), baz(X1, X2) % etc
回到你的问题:你可以使用msort/2
(或者在你正在使用的实现中调用一个稳定的排序谓词)。然后,您需要计算相同元素的运行。在SWI-Prolog中,至少你有group_pairs_by_key/2
。您可以使用它,例如如下(与同一个库中的其他谓词一起,您可以在同一链接中看到代码):
pairs_keys_values(Pairs, Sorted, Sorted),
group_pairs_by_key(Pairs, Grouped),
pairs_values(Grouped, Runs),
maplist(length, Runs, Counts)
此时,Sorted
Counts
中每个元素的出现次数(比uniq --count
更加冗长),你只需要检查一下这些都超出了你的极限。要做一些非常类似于Prolog上面的awk
调用的内容:
maplist(=<(3), Counts)
免责声明:这只是解决问题的一种方法。我决定输入它,因为它表明如果你知道已经有哪些工具可供你自己编写很多代码。
当然没有必要使用group_pairs_by_key/2
;但是,了解它非常有用,这就是我将实现联系起来的原因。对于这个问题,进行稳定排序就足够了,然后是一个简单计算同一元素连续出现次数的谓词,并且只有在所有这些运行都不长于限制时才会成功。执行该操作的谓词的基本结构与group_pairs_by_key/2
相同。
答案 2 :(得分:1)
这段代码是什么......
frequency(L,N):-getall(L,L1), max_member(A,L1),A=<N.
getall([],[]).
getall(L,N):-append([],[X1|T],L),count(X1,L,N1),getall(T,N2),append([N1],N2,N).