2015-04-23 9 views
29

Sprawa deterministycznego sukcesu jakiegoś celu Prolog okazał się czas i ponownie w — przynajmniej — następujące pytania:Making „deterministyczny sukces” Prolog celów wyraźnych

Stosowano różne metody (np. Wywoływanie pewnych błędów w zasobach lub dokładne przyglądanie się dokładnym odpowiedziom podanym w Prolog), ale wszystkie wydają się być dla mnie nieco wyładowane.

Szukam ogólnego, przenośnego i zgodnego z normą ISO sposobu, aby dowiedzieć się, czy wykonanie jakiegoś celu Prologu (który powiodło się) pozostawiło za sobą kilka punktów wyboru. Może jakiś meta-predykat?

Czy możesz wskazać mi właściwy kierunek? Z góry dziękuję!

+4

To jest * hack reklamowy ** em *** – false

Odpowiedz

13

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). 
+1

Wszyscy kochamy dobry hack. Ale, podobnie jak w przypadku Urządzenia Duffa, uwydatnia to problem z konstrukcją języka: wbudowane narzędzie służące do celów czyszczenia usuwa informacje na poziomie implementacji. – jschimpf

+2

Uważam, że to niesamowite, że ten predykat jest tak wszechstronny, że choć na powierzchni związanej z oczyszczaniem, jest wystarczająco ekspresyjny, że informacja determinizmu, która moim zdaniem jest cenna w kilku sytuacjach i dlatego powinna być dostępna, może być łatwo wyodrębniona za pomocą . – mat

+1

Oczywiście, jest "niesamowity". Mówię tylko, że jest to zły projekt;) – jschimpf

1

Istnieje brak gwarancji w setup_call_cleanup/3, że wykrywa determinizm, tj. brak punktów wyboru w sukcesie celu. Opis 7.8.11.1 Opis draft proposal mówi tylko:

c) Procedura czyszczenia jest wywoływana dokładnie jeden raz; nie później niż
po awarii G. Wcześniejsze momenty są:
Jeśli G jest prawdziwe lub fałszywe, C nazywa się w implementacji
zależny chwila po ostatnim roztworze i po ostatnim
obserwowalnym efektem G.

Więc nie ma obecnie wymóg:

setup_call_cleanup(true, true, Det=true) 

Zwraca Det = true w pierwszej kolejności. Znajduje to również odzwierciedlenie w przypadkach testowych 7.8.11.4 przykładach, że draf proposal daje, możemy znaleźć jeden przypadek testowy, który mówi:

setup_call_cleanup(true, true, X = 2). 
Either: Succeeds, unifying X = 2. 
Or: Succeeds. 

więc jej zarówno prawidłową realizację, w celu wykrycia determinizm i nie do wykrycia determinizm.

+1

Jako osoba wdrażająca zawsze zaleca się, aby wykraczała poza to, co przepisuje norma. Musisz przeczytać standard, aby zapewnić * absolutne minimum * wymagania, a jak już widzisz we wszystkich rzeczywistych implementacjach 'setup_call_cleanup/3', dostępne systemy wykraczają daleko poza to minimum, w rzeczywistości wywołując czyszczenie tak szybko, jak to możliwe! – mat

+1

To jest w porządku, chociaż wciąż nie ma powodu, by ująć odpowiedź, która pokazuje, jak używać tego predykatu w systemach, które wykraczają poza to, co przepisuje standard, a nawet dołączają taki przykład do swojej dokumentacji. – mat

+1

Wyraźnie mówię, że jest ** całkiem ** przenośny. Proszę przeczytać posty, w których głosujesz. – mat