Перевести Haskell в Prolog - найти подсписок суммы

Я пытаюсь перевести следующий код Haskell:

-- sublistSums list sum returns a list of the sublists of list that add up to sum
sublistSums :: [Int] -> Int -> [[Int]]
sublistSums [] 0 = [[]]
sublistSums [] _ = []
sublistSums (x:xs) sum = [x:tailList | tailList <- sublistSums xs (sum-x)] ++
                         sublistSums xs sum

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

?- sublistSums([[1,2,3],[4,5,6]],6).

Должен дать вам [[1,2,3]], если он свяжется, потому что 1+2+3=6.

Это то, что у меня есть до сих пор:

sublistSums([], 0) :-
    sublistSums([],0,[]).
sublistSums([], _) :-
    sublistSums([],0,[]).
sublistSums([x|xs], sum) :-
    conc(conc(x,findall(xs,(sublistSums(xs, (sum-x))),List)), sublistSums(xs, sum)).

Однако когда я вызываю функцию sublistSums, она дает мне следующее:

?- sublistSums([[1,2,3],[4,5,6]],6).
false.

Я также пробовал:

sublistSums([], 0, ReList) :-
   [[]].
sublistSums([], _, ReList) :-
   [].
sublistSums([x|xs], sum, ReList) :-
   conc(conc(x,findall(xs,(sublistSums(xs, (sum-x)),ReList),List)), sublistSums(xs, sum, ReList)).

Все еще дает мне те же результаты. Не совсем уверен, что я делаю неправильно здесь.


person RosaryLightning X    schedule 10.04.2017    source источник


Ответы (1)


В вашем коде Пролога есть несколько элементарных проблем:

  • sum — это атом Пролога, поэтому он никогда не будет унифицирован со списком.
  • в Прологе мы определяем отношения между сущностями, и вам нужны предикаты аргументы для обращения к этим сущностям.

Поэтому вы не можете перевести код дословно. Тем не менее, возможен достаточно буквальный перевод HaskellProlog, если вы, например, вводите новый аргумент для хранения возвращаемого значения исходной функции. Ваш второй фрагмент идет в этом направлении, но он не является самодостаточным, а также содержит несколько ошибок. Например, xs и sum — это атомы, тогда как переменные в Прологе начинаются с заглавной буквы или символа подчеркивания.

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


Таким образом, вместо дословного перевода я хотел бы показать вам более идиоматическое решение этой задачи на Прологе.

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

Во-вторых, задачу можно рассматривать как «фильтрацию» списка, и для этого мы используем tfilter/3, который предоставляется библиотека(reif).

Вот полное решение Prolog:

sublist_sum(Lss0, Sum, Lss) :-
        tfilter(sum_intlist(Sum), Lss0, Lss).

sum_intlist(Sum, Ls, T)  :-
        sum(Ls, #=, Sum0),
        zcompare(C, Sum0, Sum),
        eq_truth(C, T).

eq_truth(=, true).
eq_truth(<, false).
eq_truth(>, false).

Думайте об этом предикате как о связанном списке Lss0, сумме и втором списке Lss, так что все заявленные вами требования удовлетворены. Заметьте, я не говорю, какие из этих аргументов «даны», потому что любой из них может быть указан полностью, частично или вообще не указан.

Ваш пример работает по мере необходимости:

?- sublist_sum([[1,2,3],[4,5,6]], 6, Ls).
Ls = [[1, 2, 3]].

Этот пример остается в пределах исходной функции: первый и второй аргументы полностью определены, а третий используется для представления «результата».

Однако, как это типично для Пролога, мы можем также использовать более общие запросы, в которых не только "результат", но и другие части остаются неопределенными. Ясно, что это выходит далеко за рамки функций, и дословный перевод исходного кода может не иметь такого преимущества.

Например:

?- sublist_sum([[1,2,N]], 6, Ls).

В этом случае мы оставили N неуказанным и попросили Prolog рассмотреть все случаи, которые могут применяться для N.

В ответ Пролог генерирует различные возможности, представленные в виде дизъюнкции:

N = 3,
Ls = [[1, 2, 3]] ;
Ls = [],
N in inf..2,
-3+_10694#=N,
_10694 in inf..5 ;
Ls = [],
N in 4..sup,
-3+_10662#=N,
_10662 in 7..sup.

Обратите внимание, как Ls зависит от N.

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

?- sublist_sum([[A,B,C]], Sum, Ls).
Ls = [[A, B, C]],
A+B+C#=Sum ;
Ls = [],
A+B+C#=_15806,
_15806#=<Sum+ -1,
zcompare(<, _15806, Sum) ;
Ls = [],
A+B+C#=_15806,
Sum#=<_15806+ -1,
zcompare(>, _15806, Sum).

Опять же, Пролог перечисляет различные случаи.

Более того, мы можем специализировать запрос и использовать его для проверки связи:

?- sublist_sum([[1,2,3]], 5, []).
true.

Здесь мы спросили: Выполняется ли этот конкретный случай?, и Пролог отвечает true, указывая, что отношение работает именно так, как предполагалось в этом случае.

person mat    schedule 10.04.2017
comment
Для простоты есть ли способ решить проблему без использования библиотеки (reif)? - person RosaryLightning X; 11.04.2017
comment
Если вы хотите разрушить декларативное значение вашего кода, широко используемый предикат include/3 — хороший способ сделать это, поскольку он нечисто фиксирует только одну из ветвей, даже если обе могут по-прежнему применяться. Вы можете легко найти реализацию, а также реализовать ее самостоятельно. Полученный код, скорее всего, будет таким же ограниченным, как и версия Haskell. - person mat; 11.04.2017
comment
Хорошо, понял. Кроме того, я не понимаю, почему sum_intlist(Sum) вызывается с одной переменной, а для sum_intlist(Sum, Ls, T) ожидается три? Я попытался запустить его, и он выдает мне ошибку из-за этого. Извините за все вопросы. - person RosaryLightning X; 11.04.2017
comment
Это закрытие, или, как вы сказали бы в Haskell, частично примененное. Предикат tfilter/3 всегда вызывает замыкание с двумя дополнительными аргументами: элементом списка и значением истинности, чтобы решить, должен ли этот элемент быть включен или нет. Конечно, могут быть применимы оба варианта, и в таких случаях оба ответа генерируются при возврате. tfilter/3 автоматически фиксирует одну из ветвей, когда это безопасно, т. е. когда никакая другая ветвь не может применяться, потому что аргумент уже достаточно конкретизирован. - person mat; 11.04.2017
comment
sum_intlist скорее sum_intlist_t? - person false; 11.04.2017
comment
Имена переменных SWI, такие как _15806, доставляют неудобства. Хуже того, они чаще всего появляются в более общих ответах... Некоторые хорошие ответы SO могли бы быть еще лучше, с красиво изложенными компактными дизъюнкциями. Грустный! - person repeat; 29.10.2017