Пролог подсчитать количество элементов Ошибка

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

Так вот код -

count(_, [], _) := !.

count(El, [El|T], N) :-
    N1 is N + 1,
    count(El, T, N1).

count(El, [_|T], N) :-
    count(El, T, N).

check(List1, List2) :-
    count(3, List1, M),
    count(2, List2, N),
    M is N.

так что в основном я хотел бы перейти к проверке консоли ([3,4,3], [2,3,4,5,2]), и она должна возвращать true, потому что вхождения 3 в list1 совпадают с вхождениями 2 в списке2. Но вместо этого это бросает меня -

Arguments are not sufficiently instantiated. 
Exception: (10) _G1383 is _L155+1 ? creep
Exception: (9) count(3, [3, 4, 2], _G1385) ? creep
Exception: (8) count(3, [2, 3, 4, 2], _G1385) ? creep
Exception: (7) check([2, 3, 4, 2], [2, 3, 4]) ? creep 

В чем причина этого и как я могу ее решить? Я проверил все вокруг форума, и везде написано, что это должно работать. Это что-то вроде версии, или я что-то здесь упускаю?

РЕДАКТИРОВАТЬ: Использование SWI-Пролог.

EDIT2:

Работай, спасибо!

Код:

count(_, [], 0) :- !.

count(El, [El|T], N) :-
    count(El, T, N1),
    N #= N1 + 1.

count(El, [Y|T], N) :-
    El \= Y,
    count(El, T, N).

check(List1, List2) :-
    count(3, List1, M),
    count(2, List2, N),
    M #= N.

2 ответа

Решение

Вы используете предикаты, которые называются модовыми, потому что они могут использоваться только в очень специфических ситуациях. Особенно, (is)/2 не может использоваться как отношение, как вам нужно здесь.

Один из способов решить эту проблему - использовать более общие предикаты. Например, при рассуждении над целыми числами рассмотрите возможность использования ограничений CLP(FD) вашей системы Prolog, которые работают во всех направлениях.

Например, с помощью GNU Prolog вы можете устранить ошибку, если просто замените (is)/2 от (#=)/2:

считать (_, [], _).

рассчитывать (El, [El|T], N):-
    N1 #= N + 1,
    считать (El, T, N1).

рассчитывать (El, [_|T], N):-
    считать (El, T, N).

проверить (List1, List2):-
    считать (3, List1, M),
    count(2, List2, N),
    М # = Н.

Теперь мы получаем:

? - считать (3, [3, 4, 2], С).
С +1#=_1204; 
 правда; 
 ложь

(или, в зависимости от вашей системы Prolog, эквивалентный ответ).

Почему? Очевидно, что программа немного ошибочна.

Я оставляю поиск ошибки в качестве упражнения. Подсказка: M #= N выглядит подозрительно: это правда, если M равно N...

Здорово, что теперь вы работаете, используя декларативную арифметику!

У меня есть несколько небольших дополнительных комментариев о решении, которое вы получили, а именно:

считать (_, [], 0):-!.

рассчитывать (El, [El|T], N):-
    рассчитывать (El, T, N1),
    N #= N1 + 1.

рассчитывать (El, [Y|T], N):-
    El \= Y,
    считать (El, T, N).

проверить (List1, List2):-
    считать (3, List1, M),
    count(2, List2, N),
    М # = Н.

Во-первых, обратите внимание, что check/2 в коде нигде не используется, поэтому я опущу его определение в следующем.

Самый общий запрос

Когда я просматриваю код Prolog, о котором ничего не знаю, я всегда сначала пробую самый общий запрос, где все аргументы являются переменными. В идеале ответы показывают, как выглядят решения в целом.

Например, в вашем случае:

? - считать (E, Ls, C).
Ls = [], 
 С = 0.

Это - ошибочно - предполагает, что существует только одно решение вашего предиката! Это явно не предназначено.

Поэтому в качестве первого шага я рекомендую вам удалить !/0 и измените этот код на:

считать (_, [], 0).

рассчитывать (El, [El|T], N):-
    рассчитывать (El, T, N1),
    N #= N1 + 1.

рассчитывать (El, [Y|T], N):-
    El \= Y,
    считать (El, T, N).

С этим изменением мы получаем:

? - считать (E, Ls, C).
Ls = [], 
 С = 0; 
 Ls = [E], 
 С = 1; 
 Ls = [E, E], 
 С = 2; 
 Ls = [E, E, E], 
 С = 3.

Это выглядит намного лучше: теперь мы получаем несколько правильных ответов.

прекращение

Сколько ответов может дать этот предикат? В частности, есть только конечное число ответов? Если это так, мы ожидаем, что следующий запрос завершится:

? - считать (E, Ls, C), ложь.

Вы можете попробовать это, и увидите, что это на самом деле не заканчивается (по крайней мере, не очень скоро). Это хороший знак, потому что от правильной реализации count/3 Мы ожидаем нетерминации в самом общем случае!

завершенность

В идеале мы хотели бы, чтобы предикат был полным: он не должен пропускать правильные ответы.

Например:

? - считать (E, [X, Y, Z], C).
E = X, X = Y, Y = Z, 
 С = 3;
ложный.

Это действительно все решения? Я так не думаю! Конечно, есть списки длиной 3, которые отличаются от [E,E,E],

И фактически ваша программа также "знает" о них в некотором смысле:

? - считать (E, [1,2,3], C).
E = C, C = 1;
ложный.

Но опять же, это, конечно, не все случаи! Где ответы, в которых E отличается от 1?

Вы сталкиваетесь с этими проблемами, потому что вы используете немонотонный (\=)/2 сказуемое. У этого есть несколько очень трудных для понимания свойств, особенно если вы в настоящее время только начинаете изучать Пролог. Например:

?- X \ = Y, XY = ab.
ложь?- XY = ab, X \= Y.
Х = а, 
 Y = б.

Я рекомендую использовать dif/2 вместо этого, чтобы обозначить, что два условия различны, получая следующую версию:

считать (_, [], 0).

рассчитывать (El, [El|T], N):-
    рассчитывать (El, T, N1),
    N #= N1 + 1.

рассчитывать (El, [Y | T], N): -
    диф (Эл, Y),
    считать (El, T, N).

С этой версией мы получаем:

? - считать (E, [X, Y, Z], C).
E = X, X = Y, Y = Z, 
 С = 3; 
 E = X, X = Y, 
 С = 2, 
 диф (Y, Z); 
 E = X, X = Z, 
 С = 2, 
 диф (Z, Y);
и т.п.

И, в частности:

? - считать (E, [1,2,3], C).
E = C, C = 1; 
 Е = 2, 
 С = 1; 
 Е = 3, 
 С = 1; 
 С = 0, 
 диф (Е, 3), 
 диф (Е, 2), 
 диф (Е, 1).

Это охватывает все случаи, которые могут возникнуть!

Справедливое перечисление

Поскольку предикат является чистым и монотонным, мы можем использовать его для правильного перечисления ответов путем итеративного углубления.

Например:

? - длина (Ls, _), отсчет (E, Ls, C).
Ls = [], 
 С = 0; 
 Ls = [E], 
 С = 1; 
 Ls = [_G588], 
 С = 0, 
 диф (E, _G588); 
 Ls = [E, E], 
 С = 2; 
 Ls = [E, _G597], 
 С = 1, 
 диф (E, _G597) . 
 С = 2;
и т.п.

Это довольно хорошо, и показывает, что мы можем использовать это как истинное отношение, не только для подсчета, но и для генерации.

Следовательно, вы можете рассмотреть имя предиката, которое более уместно описывает, что этот предикат означает в целом. Я оставляю это как упражнение.

Хвост рекурсивная версия

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

считать (_, [], 0).
рассчитывать (El, [El|T], N):-
    N #= N1 + 1,
    считать (El, T, N1).
рассчитывать (El, [Y | T], N): -
    диф (Эл, Y),
    считать (El, T, N).

Детерминизм

В настоящее время у нас еще есть, например:

? - считать (a, [a, a, a], Cs).
Cs = 3 ;
ложь

С помощью if_/3 Вы можете улучшить детерминизм этого предиката:

: - use_module ( библиотека (reif)). считать (_, [], 0). count (E, [L | Ls], N): - if_ (E = L, N # = N1 + 1, N # = N1), count (E, Ls, N1). 

Это делает ваш предикат как минимум поддающимся индексации. Улучшите ли вы детерминизм в таких случаях, зависит от механизма индексации вашей системы Prolog.

Другие вопросы по тегам