Пролог: поиск, если 2 элемента в списке встречаются одинаково

Я новичок в Прологе и не являюсь носителем языка, поэтому, если вы меня не понимаете, извините.

Мой вопрос: как я могу узнать, отображаются ли a и b из списка одинаково?

Например, [a,a,b,b] должно дать мне true, но если одно из них окажется больше другого, оно должно дать false. пример: [a,a,a,b,b].

Кто-нибудь может мне помочь? Это то, что у меня есть до сих пор, и я знаю, что это неправильно, но я пытаюсь.

count(N,[],0).
count(N,[N|T],U) :-
   !,
   count(N,T,U1),
   U is U1+1.
count(N,[H|T],U) :-
   count(N,T,U).

occurrences([],[]).
occurrences([H|T],[[H,X]|Y]) :-
   count(H,[H|T],X),
   occurrences(T1,Y).

person Nona    schedule 14.11.2017    source источник
comment
Существуют ли другие элементы, кроме a и b?   -  person false    schedule 15.11.2017
comment
нет только а и б.   -  person Nona    schedule 15.11.2017
comment
Под выглядеть одинаково вы также подразумеваете последовательно? Другими словами, удается ли [a,b,a,b]?   -  person lurker    schedule 15.11.2017


Ответы (4)


Вот более компактная версия вхождения/5 с if_/3 и =/3:

occurrences([],_A,_B,N,N).
occurrences([H|T],A,B,N0,M0) :-
   elem_x_count_(H,A,N1,N0),
   elem_x_count_(H,B,M1,M0),
   occurrences(T,A,B,N1,M1).

elem_x_count_(E,X,New,Old) :-
   if_(E=X, New is Old+1, New=Old).

В этой версии есть только одно рекурсивное правило, которое использует elem_x_count_/4 для увеличения соответствующих аргументов счетчика, если заголовок списка соответствует A и B соответственно, или чтобы оставить их без изменений в противном случае. Предикаты вызова остаются неизменными:

occurrences(List,A,B) :-
   dif(A,B),
   occurrences(List,A,B,0,0).

or

occurrences(List) :-
   occurrences(List,a,b,0,0).

Таким образом, предикат детерминистически завершается успешно, если все три аргумента являются обоснованными (нет необходимости нажимать ; после true, так как не осталось открытых точек выбора). Вот пример запроса из старой версии с вхождениями/3:

?- occurrences([a,a,b,b],a,b).
true.

Еще одно отличие состоит в том, что количество ограничений dif/2 меньше. Например:

?- occurrences([a,a,b,b,c,c,d],X,Y).
X = a,
Y = b ;
X = a,
Y = c ;
X = b,
Y = a ;
X = c,
Y = a ;
X = b,
Y = c ;
X = c,
Y = b ;
dif(X, d),
dif(X, c),
dif(X, b),
dif(X, a),
dif(X, Y),
dif(Y, d),
dif(Y, c),
dif(Y, b),
dif(Y, a).

Только последнее решение этого запроса отличается от моей предыдущей версии. Он описывает тот же результат, но все различия, которые дважды появлялись в другой версии, теперь появляются только один раз. Другие примеры запросов дают те же ответы.

person tas    schedule 15.11.2017

Вы можете написать предикат вхождения/5, который будет истинным, если второй и третий аргументы встречаются одинаково часто в списке, который является первым аргументом. 4-й и 5-й аргументы являются соответствующими счетчиками. Тогда предикат вхождения/1 является предикатом вызова:

occurrences(List) :-
   occurrences(List,a,b,0,0).

occurrences([],_A,_B,N,N).       
occurrences([A|Xs],A,B,N0,M) :-  
   N1 is N0+1,                   
   occurrences(Xs,A,B,N1,M).     
occurrences([B|Xs],A,B,N,M0) :-
   M1 is M0+1,
   occurrences(Xs,A,B,N,M1).
occurrences([X|Xs],A,B,N,M) :-
   dif(A,X),
   dif(B,X),
   occurrences(Xs,A,B,N,M).

Вы начинаете со счетчиков в 0, и в зависимости от того, равен ли заголовок списка A или B, соответствующий счетчик увеличивается или счетчик не увеличивается, если заголовок отличается от обоих. Теперь давайте посмотрим на результаты для ваших примеров:

?- occurrences([a,a,b,b]).
true ;
false.

?- occurrences([a,a,a,b,b]).
false.

Однако я думаю, что предикат вхождения / 3, который позволяет вам указать два элемента, был бы более полезным:

occurrences(List,A,B) :-
   dif(A,B),                     
   occurrences(List,A,B,0,0).    

Тогда ваши примеры запросов будут выглядеть так:

?- occurrences([a,a,b,b],a,b).
true ;
false.

?- occurrences([a,a,a,b,b],a,b).
false.

Вы также можете спросить, какие элементы встречаются одинаково часто:

?- occurrences([a,a,b,b,c,c,d],X,Y).
X = a,
Y = b ;
X = a,
Y = c ;
X = b,
Y = a ;
X = c,
Y = a ;
X = b,
Y = c ;
X = c,
Y = b ;
dif(X, d),
dif(X, c),
dif(X, c),
dif(X, b),
dif(X, b),
dif(X, a),
dif(X, a),
dif(X, Y),
dif(Y, d),
dif(Y, c),
dif(Y, c),
dif(Y, b),
dif(Y, b),
dif(Y, a),
dif(Y, a).

Последнее решение соответствует двум элементам, которые вообще не встречаются в списке, так как оба они встречаются одинаково часто, а именно 0 раз. Если вы хотите использовать предикат в другом направлении, то есть спросить, какие существуют списки, в которых два заданных элемента появляются одинаково часто, вы должны указать цель, ограничивающую длину списка во время предиката. вызов, например:

?- length(L,_),occurrences(L,a,b).
L = [] ;
L = [_G150],
dif(_G150, b),
dif(_G150, a) ;
L = [a, b] ;
L = [b, a] ;
L = [_G116, _G119],
dif(_G116, b),
dif(_G116, a),
dif(_G119, b),
dif(_G119, a) ;
L = [a, b, _G162],
dif(_G162, b),
dif(_G162, a) ;
L = [a, _G159, b],
dif(_G159, b),
dif(_G159, a) ;
L = [b, a, _G162],
dif(_G162, b),
dif(_G162, a) ;
.
.
.
person tas    schedule 14.11.2017
comment
Может быть, также более определенная версия? - person false; 15.11.2017
comment
Большое спасибо. Это действительно полезно, и я очень ценю это. - person Nona; 15.11.2017
comment
@false: я добавил один здесь. - person tas; 15.11.2017

У вас есть только один список, поэтому сначала ваше сопоставление с образцом не удастся, кстати, я попробую так, и я не добавляю никаких объяснений, чтобы вы могли сами понять, как это работает.

count(List):-
  count(List,0,0).

count([],L,L).


count([H|T],L,R):-
  (   
   H == 'a' ->
      NewL is L + 1,
      count(T,NewL,R)
    )
     ;
   (
     H == 'b' ->
       NewRight is R + 1,
       count(T,L,NewRight)
    ).
person Luai Ghunim    schedule 14.11.2017
comment
Большое спасибо, я очень ценю это. Я думал о той же идее, но я не знал, как выбрать список, состоящий только из a и b, и сделать их равными. - person Nona; 15.11.2017

Вместо подсчета вы можете выбрать соответствующий элемент из хвоста списка — он должен существовать — во время посещения.

Базовый вариант рекурсии будет самым простым, о котором вы только можете подумать... и select/3 — это встроенная функция, которая позволит действительно компактно решить вашу проблему.

изменить

ну код простой...

occurrences([],[]).
occurrences([H|T],R) :- select(H,T,U), occurrences(U,R).

примечание: в случае успеха R будет пустым списком

person CapelliC    schedule 14.11.2017
comment
@LuaiGhunim: спасибо, я неправильно понял вопрос. Отредактирую свой ответ, так как стратегия (использование select/3) будет одинаково хорошо применяться к одному списку. - person CapelliC; 15.11.2017
comment
лол, я удалил свой комментарий, думал, что может быть два списка. - person Luai Ghunim; 15.11.2017