在prolog中过滤列表

时间:2017-05-16 21:07:57

标签: prolog

我试图将代码从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中创建类似的功能?

4 个答案:

答案 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}}的不同,在这种情况下,AB只出现一次。< / 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) .

享受通过获得的这种关系的一般性。

答案 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).