我试图将代码从Haskell重写为Prolog。
count :: Eq a => a -> [a] -> Int
count x = length . filter (x==)
f :: [Integer] -> [Integer]
f [] = []
f list = filter (\x -> count x list == 1) list
此代码返回列表包含仅在列表中出现一次的元素。
因此,如果我有列表[1,1,2,2,3,4,4,5]
,则此函数返回[3,5]
我试图在Prolog中找到过滤器构造,但似乎没有这样的事情。如何在Prolog中创建类似的功能?
答案 0 :(得分:5)
对于现有的答案,我想添加一个非常一般的答案,因为您可以在多个方向中使用它。
list_element_number/3
我从以下谓词开始,在:
之间定义关系Ls0
E
N
E
的号 Ls0
这是:
list_element_number(Ls0, E, N) :- tfilter(=(E), Ls0, Ls), length(Ls, N).
此解决方案使用library(reif)
中的tfilter/3
。谓词包含您发布的函数count
。这个谓词相对于函数的主要好处是谓词不仅可以用于那些甚至Haskell都可以轻松完成的情况,例如:
?- list_element_number([a,b,c], a, N). N = 1.
不,我们也可以在其他指示中使用它,例如:
?- list_element_number([a,b,c], X, 1). X = a ; X = b ; X = c ; false.
甚至:
?- list_element_number([a,b,E], X, 2). E = X, X = a ; E = X, X = b ; false.
甚至:
?- list_element_number([A,B,C], X, 3). A = B, B = C, C = X ; false.
即使在最常见的情况中,所有参数都是新变量:
?- list_element_number(Ls, E, N). Ls = [], N = 0 ; Ls = [E], N = 1 ; Ls = [E, E], N = 2 ; Ls = [E, E, E], N = 3 .
我们可以公平地枚举所有这样的答案:
?- length(Ls, _), list_element_number(Ls, E, N). Ls = [], N = 0 ; Ls = [E], N = 1 ; Ls = [_160], N = 0, dif(E, _160) ; Ls = [E, E], N = 2 .
list_singletons/2
使用此构建基块,我们可以按如下方式定义list_singletons/2
:
list_singletons(Ls, Singles) :- tfilter(count_one(Ls), Ls, Singles). count_one(Ls, E, T) :- list_element_number(Ls, E, Num), cond_t(Num=1, true, T).
这会使用来自cond_t/3
的{{1}}和({再次)tfilter/3
。
以下是一些示例查询。首先,您发布的测试用例:
?- list_singletons([1,1,2,2,3,4,4,5], Singles). Singles = [3, 5].
它按预期工作。
现在涉及变量的案例:
?- list_singletons([A,B], Singles). A = B, Singles = [] ; Singles = [A, B], dif(A, B).
在回溯时,所有可能性都是生成: library(reif)
成立,在这种情况下,没有元素只发生一次。 或 A = B
与<{1}}的不同,在这种情况下,A
和B
只出现一次。< / p>
作为上述查询的特例,我们可以发布:
?- list_singletons([A,A], Singles). Singles = [].
作为概括,我们可以发布:
?- length(Ls, _), list_singletons(Ls, Singles). Ls = Singles, Singles = [] ; Ls = Singles, Singles = [_7216] ; Ls = [_7216, _7216], Singles = [] ; Ls = Singles, Singles = [_7828, _7834], dif(_7828, _7834) ; Ls = [_7216, _7216, _7216], Singles = [] ; Ls = [_7910, _7910, _7922], Singles = [_7922], dif(_7910, _7922) .
享受通过logical-purity获得的这种关系的一般性。
答案 1 :(得分:3)
简单版本:
(请参阅下面@false的评论,findall/3
的版本在更复杂的查询中存在一些不一致问题,但第二版看起来不错,但绝对不是那么有效。)
filter_list(L,OutList):-findall(X, (member(X,L),count(X,L,N),N=:=1) , OutList).
count(_,[],0).
count(X,[X|T],N):-count(X,T,N1),N is N1+1.
count(X,[X1|T],N):-dif(X,X1),count(X,T,N).
谓词filter_list/2
使用findall/3
并简单地声明查找属于列表L的所有X并且count返回1并将它们存储在OutList中。
示例:
?- filter_list([1,1,2,2,3,4,4,5],L).
L = [3, 5].
您可以在不使用filter_list/2
的情况下编写findall/3
,如:
filter_list(L,OutList):- filter_list(L,OutList,L).
filter_list([],[],_).
filter_list([H|T],[H|T1],L):-count(H,L,N), N=:=1, filter_list(T,T1,L).
filter_list([H|T],T1,L):-count(H,L,N), N > 1, filter_list(T,T1,L).
答案 2 :(得分:3)
更简单的版本:
filter_list(L,OutList):-findall(X, (select(X,L, L1),\+member(X, L1)) , OutList).
?- filter_list([1,1,2,2,3,4,4,5],L).
L = [3, 5].
如果没有findall,您可以尝试
filter_list(In, Out) :- filter_list(In, _, Out).
filter_list([], [], []).
filter_list([H|T], L1, L2) :-
filter_list(T, LL1, LL2),
( member(H, LL1)
-> L1 = LL1, L2 = LL2
; (select(H, LL2, L2)
-> L1 = [H|LL1]
; L1 = LL1, L2 = [H|LL2])).
答案 3 :(得分:3)
不计算......
filter_uniques([],[]).
filter_uniques([H|T],F) :-
delete(T,H,D),
( D=T -> F=[H|R],S=T ; F=R,S=D ),
filter_uniques(S,R).
更直接地重写代码,库(yall)支持内联过滤谓词(包含/ 3的第一个参数)
filt_uniq(L,F) :-
include({L}/[E]>>aggregate(count,member(E,L),1),L,F).