Использование ограниченной переменной с ' length / 2`

вот в чем проблема:

$ swipl
Welcome to SWI-Prolog (Multi-threaded, 64 bits, Version 7.3.6-5-g5aeabd5)
Copyright (c) 1990-2015 University of Amsterdam, VU Amsterdam
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to redistribute it under certain conditions.
Please visit http://www.swi-prolog.org for details.

For help, use ?- help(Topic). or ?- apropos(Word).

?- use_module(library(clpfd)).
true.

?- N in 1..3, length(L, N).
N = 1,
L = [_G1580] ;
N = 2,
L = [_G1580, _G1583] ;
N = 3,
L = [_G1580, _G1583, _G1586] ;
ERROR: Out of global stack % after a while

(Я могу переключить порядок подзапросов, результат тот же).

думаю, мне нужно пометить N прежде чем я могу использовать его, но мне интересно, в чем проблема? Мне не удалось подавиться length/2 раньше.

4 ответов


что, вероятно, более полезно, чем немного менее недетерминированный length/2 является правильным ограничением длины списка. Вы можете найти затмение реализация it здесь, под названием len/2. При этом вы получаете следующее поведение:

?- N :: 1..3, len(Xs, N).
N = N{1 .. 3}
Xs = [_431|_482]               % note it must contain at least one element!
There is 1 delayed goal.
Yes (0.00s cpu)

затем вы можете перечислить допустимые списки, перечислив N:

?- N :: 1..3, len(Xs, N), indomain(N).
N = 1
Xs = [_478]
Yes (0.00s cpu, solution 1, maybe more)
N = 2
Xs = [_478, _557]
Yes (0.02s cpu, solution 2, maybe more)
N = 3
Xs = [_478, _557, _561]
Yes (0.02s cpu, solution 3)

или путем генерации списков с хорошим старым стандартом length/2:

?- N :: 1..3, len(Xs, N), length(Xs, _).
N = 1
Xs = [_488]
Yes (0.00s cpu, solution 1, maybe more)
N = 2
Xs = [_488, _555]
Yes (0.02s cpu, solution 2, maybe more)
N = 3
Xs = [_488, _555, _636]
Yes (0.02s cpu, solution 3)

давайте начнем с самого очевидного. Если вы переключаете цели, у вас есть:

?- length(L, N), N in 1..3.

, который имеет те же свойства расторжения как:

?- length(L, N), false, N in 1..3.

Итак, очевидно, что это не должен завершить с помощью механизма выполнения Prolog.

однако, если поставить N in 1..3 напротив, это может повлиять на прекращение. Для этого должна быть возможность с конечным средством чтобы доказать, что нет N из 4 на. Как вы можете доказать это в системе без ограничений, то есть только при наличии синтаксической унификации? Ну, ты не можешь. И length/2 is определение просто без ограничений. С library(clpfd) все тривиально, для N #>= 4, N in 1..3 не просто1. Обратите внимание также, что library(clpfd) не сотрудничает много с library(clpq) что тоже может быть интересным кандидатом.

как следствие, вам нужно будет определить свою собственную длину - для каждого пакет ограничений, который вас интересует. Это немного жаль, но в настоящее время нет универсального способа сделать это в виду. ((То есть, если вы заинтересованы и немного подумаете об этом, вы можете придумать хороший API, которого должна придерживаться каждая система ограничений. Увы, я подозреваю, что это займет еще несколько десятилетий. В настоящее время слишком много расхождений.))

Итак, вот первый наивный способ для fd_length/2:

fd_length([], N) :-
   N #= 0.
fd_length([_|L], N0) :-
   N0 #>= 1,
   N1 #= N0-1,
   fd_length(L, N1).

OK, это может быть оптимизировано, чтобы избежать лишнее choicepoint. Но есть более фундаментальная проблема: если вы определяете длину списка длины N, это создаст N переменные ограничения! Но нам нужен только один.

fd_length(L, N) :-
   N #>= 0,
   fd_length(L, N, 0).

fd_length([], N, N0) :-
   N #= N0.
fd_length([_|L], N, N0) :-
   N1 is N0+1,
   N #>= N1,
   fd_length(L, N, N1).

опять же, это не идеально по многим причинам: он может использовать алгоритм Брента, как это делают текущие системы ; и объединить его со всеми свойствами fd. Кроме того, арифметические выражения, вероятно, не являются хорошей идеей, чтобы разрешить ; но мне пришлось бы ждать (#)/1 in SWI...


1: Строго говоря, это "просто не удается" только для SICStus, SWI и YAP. Ибо в этих системах нет случайного сбоя из-за исчерпания текущего представления. То есть их неудачу всегда можно принять за честное "нет".


как насчет следующих барокко работы-на основании clpfd и мета-предикат tcount/3?

:- use_module([library(clpfd), library(lambda)]).

list_FDlen(Xs, N) :-
   tcount(\_^ =(true), Xs, N).
давайте!
?- N in 1..3, list_FDlen(Xs, N).
   N = 1, Xs = [_A]
;  N = 2, Xs = [_A,_B]
;  N = 3, Xs = [_A,_B,_C]
;  false.                             % terminates universally

?- N in inf..2, list_FDlen(Xs, N).
   N = 0, Xs = []
;  N = 1, Xs = [_A]
;  N = 2, Xs = [_A,_B]
;  false.                             % terminates universally, too

как насчет этого конкретного запроса?

?- N in 2..sup, list_FDlen(Xs, N).
   N = 2, Xs = [_A,_B]
;  N = 3, Xs = [_A,_B,_C]
;  N = 4, Xs = [_A,_B,_C,_D]
...                                   % does not terminate (as expected)

мы предлагаем clpfd-ишь вариант length/2 это адаптировано к реализации clpfd @mat.

:- use_module(library(clpfd)).
:- use_module(library(dialect/sicstus)).

:- multifile clpfd:run_propagator/2.

предикат "exported"lazy_len/2 определяется следующим образом:

lazy_len(Es, N) :-
   N in 0..sup,               % lengths are always non-negative integers
   lazylist_acc_len(Es, 0, N),
   create_mutable(Es+0, State),
   clpfd:make_propagator(list_FD_size(State,N), Propagator),
   clpfd:init_propagator(N, Propagator),
   clpfd:trigger_once(Propagator).

глобальный обработчик ограничений list_FD_size/3 постепенно изменяет свое внутреннее состояние по мере распространения ограничений. Все модификации отслеживаются и не выполняются при обратном отслеживании.

clpfd:run_propagator(list_FD_size(State,N), _MState) :- 
   get_mutable(Es0+Min0, State),
   fd_inf(N, Min),
   Diff is Min - Min0,
   length(Delta, Diff),
   append(Delta, Es, Es0),
   (  integer(N)
   -> Es = []
   ;  Delta = []
   -> true                    % unchanged
   ;  update_mutable(Es+Min, State)
   ).

lazy_len/2 решает проблему с две стороны;clpfd часть ограничения была показана выше. Сторона дерева использует prolog-coroutining чтобы пройти по списку, насколько позволяет частичный экземпляр1:

lazylist_acc_len(_, _, N) :-
   integer(N),
   !.
lazylist_acc_len(Es, N0, N) :-
   var(Es),
   !,
   when((nonvar(N);nonvar(Es)), lazylist_acc_len(Es,N0,N)).
lazylist_acc_len([], N, N).
lazylist_acc_len([_|Es], N0, N) :-
   N1 is N0+1,
   N  in N1..sup,
   lazylist_acc_len(Es, N1, N).   

примеры запросов:

?- lazy_len(Xs, N).
when((nonvar(N);nonvar(Xs)), lazylist_acc_len(Xs,0,N)),
N in 0..sup,
list_FD_size(Xs+0, N).

?- lazy_len(Xs, 3).
Xs = [_A,_B,_C].

?- lazy_len([_,_], L).
L = 2.

?- lazy_len(Xs, L), L #> 0.
Xs = [_A|_B],
when((nonvar(L);nonvar(_B)), lazylist_acc_len(_B,1,L)),
L in 1..sup,
list_FD_size(_B+1, L).

?- lazy_len(Xs, L), L #> 2.
Xs = [_A,_B,_C|_D],
when((nonvar(L);nonvar(_D)), lazylist_acc_len(_D,3,L)),
L in 3..sup,
list_FD_size(_D+3, L).

?- lazy_len(Xs, L), L #> 0, L #> 2.
Xs = [_A,_B,_C|_D],
when((nonvar(L);nonvar(_D)), lazylist_acc_len(_D,3,L)),
L in 3..sup,
list_FD_size(_D+3, L).

и, наконец, еще один запрос... ну, вообще-то два Подробнее: один вверх, другой идет вниз.

?- L in 1..4, lazy_len(Xs, L), labeling([up], [L]).
   L = 1, Xs = [_A]
;  L = 2, Xs = [_A,_B]
;  L = 3, Xs = [_A,_B,_C]
;  L = 4, Xs = [_A,_B,_C,_D].

?- L in 1..4, lazy_len(Xs, L), labeling([down], [L]).
   L = 4, Xs = [_A,_B,_C,_D]
;  L = 3, Xs = [_A,_B,_C]
;  L = 2, Xs = [_A,_B]
;  L = 1, Xs = [_A].

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