Dobra wiadomość dla wszystkich: setup_call_cleanup/3
(obecnie draft proposal dla ISO) pozwala to zrobić w całkiem przenośny i piękny sposób.
Zobacz example:
setup_call_cleanup(true, (X=1;X=2), Det=yes)
powiedzie z Det == yes
gdy nie ma więcej punktów Choice lewo.
EDIT: Pozwól mi zilustrować awesomeness tego konstruktu, czy raczej z bardzo ściśle związane orzecznika call_cleanup/2
, z prostym przykładzie:
W doskonałej CLP(B) documentation of SICStus Prolog, znajdujemy w opisie labeling/1
bardzo silny gwarancja:
Wylicza wszystkie rozwiązania przez backtracking, ale tworzy choicepoints tylko jeśli to konieczne.
Jest to naprawdę silna gwarancja, a na początku może być trudno uwierzyć, że zawsze ma. Na szczęście dla nas niezwykle łatwo jest sformułować i wygenerować systematyczne przypadki testowe w Prologu, aby zweryfikować takie właściwości, w istocie używając systemu Prolog do przetestowania samego siebie.
Zaczynamy systematycznie opisując co Boolean wyrażenie wygląda w CLP (B):
:- use_module(library(clpb)).
:- use_module(library(lists)).
sat(_) --> [].
sat(a) --> [].
sat(~_) --> [].
sat(X+Y) --> [_], sat(X), sat(Y).
sat(X#Y) --> [_], sat(X), sat(Y).
Istnieje w rzeczywistości wiele więcej przypadków, ale niech nam ograniczyć się do powyższej podgrupie CLP (B) wyrażenia na teraz.
Dlaczego używam do tego celu DCG?Ponieważ pozwala mi wygodnie opisać (podzbiór) wszystkich wyrażeń logicznych o określonej głębokości, a tym samym dość dokładnie je wyliczyć. Na przykład:
?- length(Ls, _), phrase(sat(Sat), Ls).
Ls = [] ;
Ls = [],
Sat = a ;
Ls = [],
Sat = ~_G475 ;
Ls = [_G475],
Sat = _G478+_G479 .
Zatem Używam DCG tylko do określenia ile dostępne „żetony” zostały już zużyte podczas generowania wyrażeń, ograniczając całkowitą głębokość powstałych wyrażeń.
Następnie musimy małą orzecznik pomocniczy labeling_nondet/1
, który działa dokładnie tak, jak labeling/1
, ale jest tylko prawda, jeśli wybór punkt nadal. To gdzie call_cleanup/2
przychodzi:
labeling_nondet(Vs) :-
dif(Det, true),
call_cleanup(labeling(Vs), Det=true).
Nasz przypadek Test (i przez to, że właściwie znaczy nieskończoną sekwencję drobnych przypadków testowych, które możemy bardzo łatwo opisać Prolog) ma teraz, aby zweryfikować powyższe właściwość, tj .:
Jeśli istnieje punkt wyboru, istnieje dalsze rozwiązanie.
Innymi słowy
Zestaw roztworów labeling_nondet/1
jest podzbiorem niż labeling/1
.
Miejmy zatem opisać to, co kontrprzykład powyższej nieruchomości wygląda następująco:
counterexample(Sat) :-
length(Ls, _),
phrase(sat(Sat), Ls),
term_variables(Sat, Vs),
sat(Sat),
setof(Vs, labeling_nondet(Vs), Sols),
setof(Vs, labeling(Vs), Sols).
A teraz używamy tej specyfikacji wykonywalny, aby wybrać taki kontrprzykład. Jeśli solver działa tak, jak udokumentowano, nigdy nie znajdziemy kontrprzykładu. Ale w tym przypadku, natychmiast otrzymujemy:
| ?- counterexample(Sat).
Sat = a+ ~_A,
sat(_A=:=_B*a) ? ;
Faktycznie więc właściwość czyni nie chwyt. Podziale do istoty, chociaż ma więcej rozwiązań pozostają w następującym zapytaniu Det
nie jest zjednoczony z true
:
| ?- sat(a + ~X), call_cleanup(labeling([X]), Det=true).
X = 0 ? ;
no
W SWI-Prolog, zbędne wybór-punkt jest oczywisty:
?- sat(a + ~X), labeling([X]).
X = 0 ;
false.
Jestem nie podając ten przykład, aby skrytykować zachowanie SICStus Prolog lub SWI: Nikt naprawdę nie dba o to, czy zbędny punkt wyboru pozostaje w labeling/1
, a najmniej w sztucznym przykładzie, który obejmuje zmienne kwantyfikowalne uniwersalnie (co jest w ypical dla zadań, w których używa się labeling/1
).
I am podając ten przykład, aby pokazać, jak ładnie i wygodnie gwarancje, które są udokumentowane i przeznaczone mogą być badane z takim potężnym inspekcja predykaty ...
... zakładając, że implementors są zainteresowani, aby ujednolicić swoje wysiłki , aby te predykaty faktycznie działały w ten sam sposób w różnych implementacjach!Uważny czytelnik zauważy, że poszukiwanie kontrprzykładów daje zupełnie inne wyniki, gdy jest używane w SWI-Prolog.
W nieoczekiwanym obrocie zdarzeń powyższy przypadek testowy wykrył rozbieżności w implementacjach SWI-Prolog i SICStus z call_cleanup/2
. W SWI-Prolog (7.3.11):
?- dif(Det, true), call_cleanup(true, Det=true).
dif(Det, true).
?- call_cleanup(true, Det=true), dif(Det, true).
false.
natomiast obu zapytań fail w SICStus Prologu (4.3.2).
Jest to dość typowy przypadek: gdy jesteś zainteresowany testowaniem konkretnej nieruchomości, znajdziesz wiele przeszkód, które przeszkadzają w testowaniu rzeczywistej własności.
W ISO draft proposal widzimy:
Awaria [cel porządki] jest ignorowane.
W dokumentacji SICStus z call_cleanup/2 widzimy:
Cleanup powiedzie determinately po wykonaniu jakiś efekt uboczny; w przeciwnym razie może wystąpić nieoczekiwane zachowanie .
A w SWI variant widzimy:
Sukces lub niepowodzenie Cleanup jest ignorowane
Tak więc, dla przenośności, powinniśmy właściwie napisać labeling_nondet/1
jak:
labeling_nondet(Vs) :-
call_cleanup(labeling(Vs), Det=true),
dif(Det, true).
To jest * hack reklamowy ** em *** – false