我在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]
关于此的任何想法或建议如何解决此问题?
谢谢。
答案 0 :(得分:1)
Haskell解决方案是错误的:5
,6
和7
在第二个列表中均出现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
).