如何在第二个列表中出现偶数的元素留在第一个列表中? (序言)

时间:2019-05-01 08:33:54

标签: prolog

我在haskell上有一个例子:

import Data.List
list1 = [-1, 2, 2, 3, 4, 5, 6, 7]
list2 = [-1, -1, 2, 2, 3, 3, 3, 4, 4, 4, 4]

func = [x | x <- list1, elem x [y | y <- list2, even (length (elemIndices y list2))]]

结果是[-1, 2, 2, 4](这是正确的结果,希望Prolog代码给我相同的结果)

首先,“ elemIndices”获取列表[-1, -1], ..., [2, 2], ..., [3, 3, 3], ..., ..., [4, 4, 4, 4], ...,,然后“ length”给出列表的长度,然后“ even”告诉我们长度是偶数还是奇数,然后“ elem”检查元素x(list1)在我们收集的列表中。

所以我有

list1 = [-1, 2, 2, 3, 4, 5, 6, 7]
list2 = [-1, -1, 2, 2, 3, 3, 3, 4, 4, 4, 4]

想要结果:

Result = [-1, 2, 2, 4]

关于此的任何想法或建议如何解决此问题?

谢谢。

2 个答案:

答案 0 :(得分:1)

Haskell解决方案是错误的:567在第二个列表中均出现0,偶数为0,因此它们应在解决方案中出现。 / p>

对于Prolog解决方案,您必须记住Prolog不是基于表达式的语言,您无法像在Haskell中那样轻松地嵌套表达式。相反,您必须将程序分解为基本步骤,并将这些部分放到一个整体上。这可能很乏味,但是除其他功能外,它还允许您一个个地彻底解决子问题并进行测试。

因此,让我们开始收集列表中某个元素的出现:

% list_element_occurrences(List, Element, Occurrences).
% Occurrences is a list of occurrences of Element in List
list_element_occurrences([], _Element, []).
list_element_occurrences([Element|Elements], Element, [Element|Occurrences]) :-
    list_element_occurrences(Elements, Element, Occurrences).
list_element_occurrences([X|Elements], Element, Occurrences) :-
    dif(X, Element),
    list_element_occurrences(Elements, Element, Occurrences).

这符合我们的期望吗?

?- list_element_occurrences([-1, -1, 2, 2, 3, 3, 3, 4, 4, 4, 4], -1, Occurrences).
Occurrences = [-1, -1] ;
false.

?- list_element_occurrences([-1, -1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3, Occurrences).
Occurrences = [3, 3, 3] ;
false.

看起来不错,让我们继续。我们真正感兴趣的不是出现列表,而是出现次数是偶数还是奇数:

% list_element_even_occurrences(List, Element).
% Element occurs an even number of times in List.
list_element_even_occurrences(List, Element) :-
    list_element_occurrences(List, Element, Occurrences),
    length(Occurrences, NumberOfOccurrences),
    NumberOfOccurrences mod 2 =:= 0.

测试:

?- list_even_occurrences([-1, -1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3).
false.

?- list_even_occurrences([-1, -1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 4).
true ;
false.

好。让我们同时执行双重操作:

% list_element_odd_occurrences(List, Element).
% Element occurs an odd number of times in List.
list_element_odd_occurrences(List, Element) :-
    list_element_occurrences(List, Element, Occurrences),
    length(Occurrences, NumberOfOccurrences),
    NumberOfOccurrences mod 2 =:= 1.

构造中间列表然后计算其长度并不是真正必要的。我们可以直接计算元素。如果需要,可以稍后进行此简化。 Prolog与Haskell的不同之处在于,Haskell在计算其长度之前实际上并未分配整个列表。

无论如何,现在我们只需要使用这些谓词来确定应该从列表中选择哪些元素(我对此处的命名感到不满意):

% list_list_even_occurrences(List, List2, EvenOccurrences).
% EvenOccurrences is the list of elements of List that occur in List2 an even
% number of times.
list_list_even_occurrences([], _List2, []).
list_list_even_occurrences([X|Xs], List2, [X|EvenOccurrences]) :-
    list_element_even_occurrences(List2, X),
    list_list_even_occurrences(Xs, List2, EvenOccurrences).
list_list_even_occurrences([X|Xs], List2, EvenOccurrences) :-
    list_element_odd_occurrences(List2, X),
    list_list_even_occurrences(Xs, List2, EvenOccurrences).

这给出了:

?- list_list_even_occurrences([-1, 2, 2, 3, 4, 5, 6, 7], [-1, -1, 2, 2, 3, 3, 3, 4, 4, 4, 4], EvenOccurrences).
EvenOccurrences = [-1, 2, 2, 4, 5, 6, 7] ;
false.

现在,您可以考虑将list_list_even_occurrences/3的定义替换为基于findall/3的定义,并可能在内联中扩展辅助谓词。

答案 1 :(得分:0)

这里的问题是您进行如下检查:

length(Zs0, N)

因此,这意味着您指定元素的数量应与N 相等,而不是本身。对于我来说,还是很不清楚为什么仍要使用N参数,因为在Haskell示例中,您只需要检查列表的长度是否为偶数即可。

Haskell程序的几乎字面翻译可能是这样的:

p(Xs, Ys, Zs) :-
    findall(
        Y,
        (
            member(Y, Ys),
            findall(Y, member(Y, Ys), Ts),
            length(Ts, NT),
            0 is NT mod 2
        ),
        Ts
    ),
    list_to_set(Ts, STs),
    findall(X, (member(X, Xs), member(X, STs)), Zs).

就像@IsabelleNewbie指出的那样,您的Haskell函数不会给出xs中在ys中出现偶数次的元素列表,它产生的元素在ys中至少出现 次,甚至偶数次。我们可以将功能更新为:

func = filter (even . length . flip filter list2 . (==)) list1

和等价的Prolog谓词将是:

p(Xs, Ys, Zs) :-
    findall(
        X,
        (
            member(Y, Ys),
            findall(Y, member(Y, Ys), Ts),
            length(Ts, NT),
            0 is NT mod 2
        ),
        Zs
    ).