Есть ли способ суммировать только целые числа в списке пар, которые содержат букву и целое число в Прологе? - PullRequest
2 голосов
/ 22 апреля 2019

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

[[a, 1], [b, 2], [c, 3], [d, 4]]

Я пробовал что-то вроде этого, так как это напоминает обычнуюфункция суммирования:

sum([], 0).
sum([[_,Head]|[_,Tail]], Sum) :-
    sum([_,Tail], Sum2),
    Sum is Head+Sum2.

При вызове:

sum([[a, 1], [b, 2], [c, 3], [d, 4]], Total),
write('Sum = '), write(Total).

Но это не работает.Он выводит значение false, когда должен вывести сумму, которая здесь равна 10.

Ответы [ 3 ]

1 голос
/ 22 апреля 2019

При попытке определить предикат sum/2 вы неправильно обрабатываете списки списков.Попробуйте:

sum(Lists, Sum) :-
    sum(Lists, 0, Sum).

sum([], Sum, Sum).
sum([[_,N]| Lists], Sum0, Sum) :-
    Sum1 is Sum0 + N,
    sum(Lists, Sum1, Sum).

В этой версии используется аккумулятор , чтобы включить хвостово-рекурсивное определение.Пример звонка:

| ?- sum([[a, 1], [b, 2], [c, 3], [d, 4]], Sum).

Sum = 10
yes
1 голос
/ 22 апреля 2019

Всякий раз, когда цель не достигает цели, которую вы ожидаете достичь, рассматривайте это как возможность выучить (краткая форма для логики заработать = заработать логику). В конце концов, это пролог, означавший Программирование в логике . Так где же логика в вашей программе?

На данный момент ваша программа не работает, но вы ожидали, что она будет успешной. Где виновник? Давайте обобщим вашу программу так, чтобы полученная программа все еще не работала, но была намного меньше. Существует два простых способа обобщить программу:

  • удалить цели (добавив префикс *)

  • удалить термины (заменив term на <b>_<s>/&#42;term&#42;/</s></b>

Мы можем сделать это довольно слепо. Не нужно понимать вашу программу. Просто проверьте, что цель все еще не достигнута. Вот что я придумал с первой попытки:

:- op(950, fy, *).
* _G_0. % ignore the argument _G_0

sum([], <b>_<s>/*0*/</s></b>).
sum([<b>_<s>/*[_,Head]*/</s></b>|[_,Tail]], Sum) :-
    * <s>sum([_,Tail], Sum2)</s>,
    * <s>Sum is Head+Sum2</s>.

?- sum([<b>_<s>/*[a, 1]*/</s></b>, <b>_<s>/*[b, 2]*/</s></b>, <b>_<s>/*[c, 3]*/</s></b>, <b>_<s>/*[d, 4]*/</s></b>], Total).
false.                            % gnah - still fails

Одна проблема должна находиться в оставшейся видимой части. Слишком сложно понять? Позвольте Прологу объяснить это вам, запросив самый общий запрос :

| ?- sum(Xs, Sum).
   Xs = []
;  Xs = [_A,_B,_C].

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

Вернемся к исходной программе и зададим самый общий запрос:

?- sum(Os, Total).
   Os = [], Total = 0
;  false.

О, нет , есть только одно решение. И даже ни одного решения для sum([_|_], Total).

Итак, давайте снова обобщим программу, но теперь относительно этой неудачной цели:

sum([], <b>_<s>/*0*/</s></b>).
sum([<b>_<s>/*[_,Head]*/</s></b>|[_,Tail|<b>_<s>/*[]*/</s></b>]], Sum) :-
    sum([_,Tail], Sum2),
    * <s>Sum is Head+Sum2</s>.

?- Os = [_|_], sum(Os, Total).
false.

В этой части должна быть еще одна ошибка. И на самом деле, цель sum([_,Tail], Sum2) является виновником: речь идет о списке ровно из двух элементов, но правило требует как минимум три

Фактические исправления см. В других ответах.

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

1 голос
/ 22 апреля 2019

Я думаю, что это может помочь разделить это на две задачи:

  1. создать новый список второго элемента каждого подсписка; и
  2. Подведите итог этого списка.

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

Мы можем получить список второго элемента подсписков с помощью:

item2list([], []).
item2list([[_, X|_]|T], [X|T2]) :-
    item2list(T, T2).

или мы можем использовать maplist/3 [swi-doc] и nth1/3 [swi-doc] :

item2list(L1, L2) :-
    maplist(nth1(2), L1, L2).

или мы можем написать item2list в терминах findall/3 [swi-doc] и member/2 [swi-doc ]

item2list(L1, L2) :-
    findall(X, member([_,X|_], L1), L2).

хотя здесь предикат не является двунаправленным.

Например:

?- item2list([[a, 1], [b, 2], [c, 3], [d, 4]], L).
L = [1, 2, 3, 4].

Я оставляю подведение итогов этого списка в качестве упражнения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...