2009-11-24 4 views
2

Я бы хотел определить предикат членов.members predicate in Prolog

членов (A, B) означает, что все члены списка A являются членами списка B. top (N) определяет, как долго может быть A.

Это моя попытка:

top(5). 

members([X], L):- 
    member(X, L). 
members([X| Xs], L):- 
    member(X, L), 
    members(Xs, L), 
    length(Xs, M), 
    top(N), 
    M < N. 

Я хотел бы использовать его следующим образом:

members(L, [1,2,3]). 

Проблема с моей реализации является то, что если я; чтобы получить новые ответы, я закончу с ошибкой: Из локального стека

?- members(I, [1,2,3]). 
I = [1] ; 
I = [2] ; 
I = [3] ; 
I = [1, 1] ; 
I = [1, 2] ; 
I = [1, 3] ; 
I = [1, 1, 1] ; 
I = [1, 1, 2] ; 
I = [1, 1, 3] ; 
I = [1, 1, 1, 1] ; 
I = [1, 1, 1, 2] ; 
I = [1, 1, 1, 3] ; 
I = [1, 1, 1, 1, 1] ; 
I = [1, 1, 1, 1, 2] ; 
I = [1, 1, 1, 1, 3] ; 
;ERROR: Out of local stack 

Как я могу изменить свой код, чтобы предотвратить это из памяти?

ответ

5

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

members([X], L):- 
    member(X, L). 
members([X|Xs], L):- 
    length(Xs, M), 
    top(N), M < N, 
    member(X, L), 
    members(Xs, L). 

... не так хорошо, как мы получим это:

L = [3, 1, 2, 3, 3] ; 
L = [3, 2, 2, 3, 3] ; 
L = [3, 3, 2, 3, 3] ; 
L = [3, 1, 3, 3, 3] ; 
L = [3, 2, 3, 3, 3] ; 
L = [3, 3, 3, 3, 3] ; 
ERROR: Out of global stack 

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

Проблема в том, что вы создаете список сверху вниз. Другими словами, мы определяем список следующим образом: List = [Head|Tail], где мы оговариваем некоторые ограничения на Head и state, что Tail состоит из списка элементов, определяемых теми же ограничениями и ограниченных базовым случаем. Это означает, что, хотя мы находимся в середине рекурсивного вызова, у нас есть только доступ к Head - мы не можем получить доступ к содержимому Tail, поскольку он строится только после того, как интерпретатор прошел весь путь вниз и достиг основного футляра (т.е. members([X], L) :-), а затем последовательно добавляет каждый хвост к своей голове, пока не будет создан окончательный список.

Это может выглядеть, как мы имеем доступ к длине, так как length/2 вызов сидит там в середине рекурсивного предиката, однако, так как переменная передается в length/2 для списка на данном этапе не связан ни к чему, Prolog ждет, пока он не закончит рекурсивные вызовы ниже этой точки перед вычислением длины. Проблема, конечно, в том, что проверка длины - это то, что ограничивает рекурсию, поэтому она будет продолжаться до тех пор, пока не закончится память.

Хотя рекурсия сверху вниз имеет тенденцию к построению предикатов Prolog по умолчанию, как показывает этот пример, иногда нам нужен доступ к структуре данных, которую мы создаем. Решение заключается в использовании восходящей рекурсии. Это реализовано в Prolog с помощью предиката аккумулятора, который начинается с пустого списка и продолжает строить список до один за другим, передавая список аккумуляторов (который является полностью наземным списком) через рекурсивный предикат.Вот как я бы написать аккумуляторный предикат для этой задачи:

members(I,L) :- 
    members_accumulator([],I,L). 

members_accumulator(Tail,I,L) :- 
    length(Tail, M), 
    top(N), 
    M < N, 
    member(X, L), 
    members_accumulator([X|Tail],I,L). 
members_accumulator(I,I,_). 

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

Получение головок вокруг рекурсии в Прологе, и когда вам нужно использовать восходящую рекурсию, а не сверху вниз, это нетривиальный подвиг. На самом деле у меня не было полного понимания, пока я не прочитал The Art of Prolog. Также должно быть много информации о аккумуляторах онлайн.

1

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

+0

Комментарий не дал никакого решения. –

4

Вот альтернативная реализация, которая не требует вычисления длины списка. Здесь N - это длина списка A. Это решение дает все ответы, не выходя из стека. выполнение

members([X],L,1) :- member(X,L). 
members([H|T],L,N) :- N>1 , member(H,L) , N1 is N-1, members(T,L,N1). 

Пример:

?- members(L,[1,2,3],5). 
L = [1, 1, 1, 1, 1] ; 
L = [1, 1, 1, 1, 2] ; 
L = [1, 1, 1, 1, 3] ; 
L = [1, 1, 1, 2, 1] ; 
... 
L = [3, 3, 3, 1, 2] ; 
L = [3, 3, 3, 3, 1] ; 
L = [3, 3, 3, 3, 2] ; 
L = [3, 3, 3, 3, 3] ; 
No 
+0

Вы можете выбрать код и нажать ctrl + k для его отступов; тогда код получит правильное форматирование. – Stephan202

1

Использование maplist/2, lambdas, и членство в предикат memberd/2 и просто написать:

:- use_module(library(lambda)). 

members(As,Bs,N) :- 
    length(Xs,N), 
    append(As,_,Xs), 
    maplist(Bs+\A^memberd(A,Bs), As). 

Пример запроса с сокращенным последовательности ответа:

?- members(As,[1,2,3],5). 
As = [   ] ; 
As = [  1] ; As = [  2] ;   As = [  3] ; 
As = [  1,1] ; As = [  1,2] ; /* ... */ As = [  3,3] ; 
As = [ 1,1,1] ; As = [ 1,1,2] ; /* ... */ As = [ 3,3,3] ; 
As = [ 1,1,1,1] ; As = [ 1,1,1,2] ; /* ... */ As = [ 3,3,3,3] ; 
As = [1,1,1,1,1] ; As = [1,1,1,1,2] ; /* ... */ As = [3,3,3,3,3] ; 
false. 

Выше запрос универсально завершается. Посмотрим на размер набора решений:

 
?- setof(As,members(As,[1,2,3],5),Ass), length(Ass,N_Solutions). 
Ass   = [[],[1],[1,1],[1,1,1],[1,1,1|...],[1,1|...],[1|...],[...|...]|...], 
N_Solutions = 364. 

?- 364 =:= 1 + 3 + 3^2 + 3^3 + 3^4 + 3^5. 
true.