czwartek, kwietnia 10, 2008

Prolog nie jest taki zły

W poniedziałek stoczyłem prawdziwą batalię, usiłując stworzyć poprawnie działające predykaty wyższego rzędu rekurencyjnie przechodzące po termie aż zajdzie jakiś warunek. Byłoby więc to coś w rodzaju metamorfizmu. Moje wysiłki spełzły jednak na niczym, skończyłem wieczorem z funkcją:

mapRec( Pred, Arg, OutArg ) :-
   % tworzymy nowy predykat pomocniczy
   gensym( partial, UNIQ ),
    Left =.. [ UNIQ, X, Y ],
    PartialMapRec = ( Left :- mapRec( Pred, X, Y ) ),
    asserta( PartialMapRec, Ref ),
    % rozkładamy term wejściowy
    Arg =.. [ Comp, Op | Args ],
    map( UNIQ, Args, Args2 ), % dla termów atomowych mamy podstawę indukcji: map    nic nie robi na pustej liście
    % składamy term ponownie
    Arg2 =.. [ Comp, Op | Args2 ],
    % aplikujemy Pred i zwracamy wynik
    F =.. [ Pred, Arg2, OutArg ],
    F,
    erase( Ref ), % kasujemy predykat pomocniczy
    !.

mapRec( _, Arg, Arg ) :-
    write( final ), write(' '), write( Arg ), nl,
    final( Arg ), !.

Najogólniej rzecz biorąc, nie działała ona tak jak powinna. To znaczy coś tam niby liczyła, ale źle, a co gorsze była okropnie długa (jak widać) i beznadziejna do debugowania. Ponadto wymagała ingerencji w bazę predykatów Prologa, co jest moim zdaniem brzydkie i psuje trochę wydajność. Zakończyłem nieudaną sesję programistyczną mocno zniesmaczony Prologiem, a konkretnie jego odpornością na próby metaprogramowania.

Dzisiaj jednak dokopałem się do paru predykatów, które znacząco uprościły konstrukcję mapRec'a:
mapRec( Pred, Arg, OutArg ) :-
    Arg =.. [ Comp, Op | Args ],
    maplist( mapRec( Pred ), Args, Args2 ),
    Arg2 =.. [ Comp, Op | Args2 ],
    apply( Pred, Arg2, OutArg ),
    !.

mapRec( _, Arg, Arg ) :-
    write( final ), write(' '), write( Arg ), nl,
    final( Arg ), !.

Nie taki Prolog straszny jak go malują :-)

Bazując na poznanej wiedzy napisałem wreszcie simp'a, czyli predykat upraszczający wyrażenia arytmetyczne. Jest to jedno z zadań na pracownię z Programowania w Logice. Wyszedł niedługi i raczej nie ma zbyt wielu błędów :-) Leży tutaj.

Do uruchomienia potrzebny jest interpreter Prologa. Ja używam SWI-Prologa, ale program powinien działać na każdym sensownym interpreterze.

Dla tych którzy nie mieli nigdy doczynienia z Prologiem, oto jak wygląda przykładowa sesja:

?- [simp].
% simp compiled 0.00 sec, 11,264 bytes

Yes
?- simp( x-x, EXP ).

EXP = 0 ;;

No
?- simp( x-x*1*2*3, EXP ).

EXP = x* -5 ;;

No
?- simp( 1*2*(1+1+1)*x-x*1*2*3, EXP ).

EXP = 0 ;;

No
?- simp( x*(y+z*(1+p)-p*(x-z)), EXP ).

EXP = - (p* (x*x))+ (p* (x*z)*2+ (x*y+x*z)) ;;

No
?-
% halt


Choć Prolog bywa wkurzający, to jednak przyjemnie jest nauczyć się czegoś nowego.

Brak komentarzy:

Prześlij komentarz