Пролог - список в порядке убывания

Я пытаюсь написать функцию - decListRange(X,List), которая дает список в диапазоне [X-1:1] в порядке убывания. Например -
decListRange(9,List).

Дам -

List = [8,7,6,5,4,3,2,1].

Я пробовал следующее, но он переходит в бесконечный цикл:

decListRange(1,[]) :- !. 
decListRange(X,[H|Rest]) :-
    H  = X-1, NextX = X - 1 ,decListRange(NextX,Rest).

person URL87    schedule 17.01.2013    source источник


Ответы (1)


У вас есть две проблемы. Первый реальный заключается в том, что вам нужно использовать is вместо =:

H is X-1

Это необходимо для запуска арифметической оценки. Ваша вторая проблема не является реальной проблемой, но говорит о большем недоразумении, которое заключается в том, что H и NextX эквивалентны. Поскольку в Prolog есть только привязки, а не "назначаемые", как это было раньше, вам никогда не понадобится создавать две "переменные" с одной и той же привязкой. Нет состояния, которое вы могли бы изменить позже.

Очистив оба, вы получите следующее:

decListRange(1, []) :- !.
decListRange(X, [H|Rest]) :-
  X > 1,
  H is X-1,
  decListRange(H, Rest).

Изменить 2: реализация clpfd

:- use_module(library(clpfd)).

declist(N, L) :- N == 1, !, L = []. % green cut
declist(1, []). 
declist(N, [N1|Ns]) :- 
  N #> 1, 
  N1 #= N - 1, 
  declist(N1, Ns).

У этого есть свойства, которые @false упоминает ниже в комментариях:

?- declist(3, L).
L = [2, 1] ;
false.

?- declist(3, [2,1]).
true ;
false.

?- declist(N, [3,2,1]).
N = 4.

?- declist(N, X).
N = 1,
X = [] ;
N = 2,
X = [1] ;
N = 3,
X = [2, 1] ;
N = 4,
X = [3, 2, 1] ;
N = 5,
X = [4, 3, 2, 1] .

Правка: короткая интермедия о разнице между = и is.

В процедурных языках = почти всегда является синтаксисом для присвоения определенного значения переменной. В Прологе переменные являются привязками, и однажды установленные, они не могут быть изменены напрямую путем переназначения переменной другого значения. Вместо этого они работают больше как переменные в математике и логике, где переменная «заменяет» интересные значения, но сами эти значения в основном неизменяемы. В Прологе = по существу просит механизм унификации установить привязки. Итак, если бы вы сделали что-то вроде этого:

?- name(X, Y) = name(bob, tony).

Пролог отвечает связыванием переменных:

X = bob,
Y = tony.

Как только эти привязки существуют, противоречащие привязки потерпят неудачу, а утвердительные привязки будут успешными:

?- name(X, Y) = name(bob, tony), X = bob.
X = bob,
Y = tony.

?- name(X, Y) = name(bob, tony), X = william.
false.

Сам алгоритм объединения ничего не знает об арифметике. У этого есть приятный побочный эффект: вы можете использовать любое выражение в чистом виде. Например:

?- Expr = X + 3, Z + Q = Expr.
Expr = Z+3,
X = Z,
Q = 3.

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

?- X = 4, Expr = X + 3, Z + Q = Expr.
X = 4,
Expr = 4+3,
Z = 4,
Q = 3.

Другой взгляд на это состоит в том, что Пролог считает + просто еще одним оператором, поэтому X+3 — это такой же факт, как и add(X, 3), который не обязательно имеет какое-то особое значение. Как бы вы ни смотрели на это, оператор is/2 существует для применения арифметических рассуждений и получения значения:

?- X = 4, Expr is X + 3.
X = 4,
Expr = 7.

Обратите внимание, что Expr имеет вычисленное значение, но не исходную структуру:

?- X = 4, Expr is X + 3, Z + Q = Expr.
false.

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

person Daniel Lyons    schedule 17.01.2013
comment
Спасибо. Не могли бы вы вкратце объяснить, чем отличаются bw is и =? - person URL87; 17.01.2013
comment
Я добавил некоторые пояснения. Надеюсь, поможет! - person Daniel Lyons; 18.01.2013
comment
@false Я думаю, было бы приятно и познавательно увидеть вашу реализацию. Я уверен, что смогу решить проблему зацикливания, но я не вижу, как сделать перечисление decListRange(N, L). - person Daniel Lyons; 18.01.2013
comment
У тебя почти получилось. Перечисление возможно прямо с помощью библиотеки (clpfd). Без этого правильно запрограммировать это - кошмар. Подумайте об этом: многие реализации не полностью правильно реализовали length/2. См. это сравнение - person false; 18.01.2013
comment
@false Я добавил эту версию. Я все еще получаю ложный ложный результат со всеми полностью созданными аргументами. Есть ли разрез, который мне не хватает? - person Daniel Lyons; 18.01.2013
comment
@false Исправлено отключение по одному. Я имею в виду ложную точку выбора после первого true в declist(3, [2,1]). Это не имеет значения, я просто не знаю, куда поставить зеленый разрез. - person Daniel Lyons; 18.01.2013
comment
Эта точка выбора не делает определение неверным или ложным. Это просто неприятность. Вы не можете поставить разрез там. Вам необходимо приложить соответствующий тест только для чтения. Например. declist(N,L) :- N == 1, !, L = []. перед другими пунктами. Подробнее см. prolog-cut. - person false; 18.01.2013
comment
Я имею в виду посторонние, а не ложные. Я понимаю, что это не влияет на правильность. - person Daniel Lyons; 18.01.2013