2015-06-06 17 views
5

Buduję parser i generator dla dat i godzin. W zwykłym języku programowania będą one pisane osobno. W Prolog + CLP (FD) mogę napisać 1 predykat, który wykonuje oba :-)Ograniczenie nie jest propagowane po utworzeniu elementów listy

W moim przypadku użycia często ma sens parsować liczbę cyfr i konwertować na liczbę całkowitą lub generować liczbę cyfr na podstawie danej liczby całkowitej.

Mój problem polega na tym, że clpfd:run_propagator/2 nie jest wywoływany, gdy tworzone są pojedyncze cyfry, pomimo moich deklaracji z użyciem clpfd:init_propagator/2. Czy istnieje sposób, aby to zrobić, czy też popełniam błąd w mojej definicji clpfd_digits/2?

Kod realizowane w SWI-Prolog:

:- use_module(library(apply)). 
:- use_module(library(clpfd)). 

:- multifile(clpfd:run_propagator/2). 

day(D) --> {clpfd_digits(D, [D1,D2])}, digit(D1), digit(D2). 

digit(D) --> [C], {code_type(C, digit(D))}. 

clpfd_digits(N, Ds):- 
    clpfd:make_propagator(clpfd_digits(N, Ds), Prop), 
    clpfd:init_propagator(N, Prop), 
    clpfd:init_propagator(Ds, Prop), 
    forall(
    member(D, Ds), 
    clpfd:init_propagator(D, Prop) 
), 
    clpfd:trigger_once(Prop). 

clpfd:run_propagator(clpfd_digits(N, Ds), MState):- 
    ( maplist(is_digit0, Ds) 
    -> clpfd:kill(MState), 
     digits_to_nonneg(Ds, N) 
    ; integer(N) 
    -> clpfd:kill(MState), 
     nonneg_to_digits(N, Ds) 
    ; true 
). 

digits_to_nonneg([], 0):- !. 
digits_to_nonneg(Ds, N):- 
    maplist(char_weight, Chars, Ds), 
    number_chars(N, Chars). 

char_weight(Char, D):- 
    char_type(Char, digit(D)). 

nonneg_to_digits(0, []):- !. 
nonneg_to_digits(N, Ds):- 
    atom_chars(N, Chars), 
    maplist(char_weight, Chars, Ds). 

is_digit0(D):- integer(D), between(0, 9, D). 

Przykład zastosowania:

?- string_codes("12", Cs), phrase(day(D), Cs). 
Cs = [49, 50], 
clpfd_digits(D, [1, 2]). 

Jak widać ograniczenie nie jest obliczana w celu uzyskania wartości D.

+2

Zobacz [tę odpowiedź] (http://stackoverflow.com/a/28442760/772868) dla metody clpfd, aby rozwiązać powiązany problem. – false

+1

Co spodziewasz się zyskać w porównaniu do 'when ((ziemia (kody), nonvar (N)), liczba_znaków (N, kody))'? Oba są słabe, a nie relacje - o to chodzi. – false

+0

@false To przychodzi dość blisko! Jednak nie zajmuje się zerami dopełnienia, np. 'Fraza (dzień (2)," 02 ")'. –

Odpowiedz

4

+1 za używanie ograniczeń CLP (FD) dla tego zadania!

forall/2 i wiązania nie mieszają się bardzo dobrze, ponieważ cofanie powoduje oderwanie zaksięgowanych ograniczeń.

Twój przykład działa zgodnie z oczekiwaniami:

flip_init(Prop, D) :- clpfd:init_propagator(D, Prop).

i korzystania maplist(flip_init(Prop), Ds) zamiast forall/2.

Następnym problemem jest to, że digits_to_nonneg([1,2], N) po prostu nie działa, ale nie ma to związku z rzeczywistym wyzwalaniem wiązań, które odbywa się zgodnie z oczekiwaniami. (Nawiasem mówiąc: Korzystanie ograniczeń, może być w stanie uprościć kod, dzięki czemu można korzystać z jednego predykatu w obu kierunkach).

Ponadto, można użyć in/2 zamiast between/3: D in 0..9. Jest to często przydatne, jeśli chcesz użyć go jako ograniczenia, a nie tylko testu.

+0

Czy któraś z trzech rzeczy w górze faktycznie ** przetworzyła ** wynikowy kod? Wciąż jest w nim wiele dalszych błędów. Jak dwa cięcia. – false

+0

Na pewno nie: po udzieleniu odpowiedzi na faktyczne pytanie (* wywołanie * propagatora), nie próbowałem nawet poprawić pierwszego błędu, który znalazłem po tym ('digits_to_nonneg/2' nieoczekiwanie nieoczekiwanie zawiodło), znacznie mniej nawet dalej. Pisanie poprawnych propagatorów z pewnością warte jest kilku pytań. – mat