Решение Haskell неверно: 5
, 6
и 7
каждый имеет 0 вхождений во втором списке, а 0 - четное, поэтому они должны встречаться в решении.
Что касается решения 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.
Нет необходимости создавать промежуточный список и затем вычислять его длину; мы могли бы просто посчитать элементы напрямую. Вы можете сделать это упрощение позже, если хотите. Пролог отличается от 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
и, возможно, расширении вспомогательных предикатов в строке.