Sök…


Introduktion

Många moderna Prolog-system är i kontinuerlig utveckling och har lagt till nya funktioner för att hantera klassiska brister i språket. Tyvärr introducerar många Prolog-läroböcker och till och med undervisningskurser fortfarande bara den föråldrade prologen. Detta ämne är avsett att illustrera hur modern Prolog har övervunnit några av problemen och ganska grova syntaxen som förekommer i äldre Prolog och fortfarande kan introduceras.

CLP (FD) för heltals aritmetik

Traolog har traditionellt utfört aritmetik med operatörerna is och =:= . Flera nuvarande Prologs erbjuder emellertid CLP (FD) (Constraint Logic Programming over Finite Domains) som ett renare alternativ för heltals aritmetik. CLP (FD) är baserat på att lagra de begränsningar som gäller för ett heltal och kombinera dem i minnet.

CLP (FD) är en förlängning i de flesta Prologs som stöder den, så måste laddas uttryckligen. När den har laddats kan syntaxen #= ta plats för båda is och =:= . Till exempel i SWI-Prolog:

?- X is 2+2.
X = 4.

?- use_module(library(clpfd)).
?- X #= 2+2.
X = 4.

Till skillnad från is #= kunna lösa enkla ekvationer och förena i båda riktningarna:

?- 4 is 2+X.
ERROR: is/2: Arguments are not sufficiently instantiated

?- 4 #= 2+X.
X = 2.

CLP (FD) tillhandahåller en egen generatorsyntax.

?- between(1,100,X).
X = 1;
X = 2;
X = 3...

?- X in 1..100.
X in 1..100.

Observera att generatorn faktiskt inte körs: endast områdesbegränsningen lagras, redo att senare begränsningar kan kombineras med den. Generatorn kan tvingas köra (och brute kraftbegränsningar) med hjälp av label :

?- X in 1..100, label([X]).
X = 1;
X = 2;
X = 3..

Att använda CLP kan tillåta viss intelligent minskning av brute force-fall. Använd till exempel antik arithmetik i gammal stil:

?- trace.
?- between(1,10,X), Y is X+5, Y>10.
...
Exit: (8) 6 is 1+5 ? creep
Call: (8) 6 > 10 ? creep
...
X = 6, Y = 11; ...

Prolog slingrar fortfarande genom värdena 1-5 även om det är matematiskt bevisbart från de givna villkoren att dessa värden inte kan vara användbara. Använda CLP (FD):

?- X in 1..10, Y #= X+5, Y #> 10.
X is 6..10,
X+5 #= Y,
Y is 11..15.

CLP (FD) gör omedelbart matematiken och räknar ut tillgängliga intervall. Om du lägger till label([Y]) får X endast slinga genom användbara värden 6..10. I det här leksaksexemplet ökar inte detta prestanda eftersom algebrabehandlingen tar så lång tid som slingan skulle ha gjort med så litet intervall som 1-10; men när ett större antal nummer behandlas kan detta värdefullt minska beräkningstiden.

Stöd för CLP (FD) kan varieras mellan Prologs. Den erkända bästa utvecklingen av CLP (FD) finns i SICStus Prolog, som är kommersiellt och dyrt. SWI-Prolog och andra öppna Prologs har ofta en del implementering. Visual Prolog inkluderar inte CLP (FD) i sitt vanliga bibliotek, även om tilläggsbibliotek finns tillgängliga.

Forall istället för feldrivna slingor

Vissa "klassiska" Prolog-läroböcker använder fortfarande den förvirrande och felbenägna misslyckade syntaxen där en fail konstruktion används för att tvinga backtracking att tillämpa ett mål för alla värden hos en generator. För att till exempel skriva ut alla siffror upp till en given gräns:

fdl(X) :- between(1,X,Y), print(Y), fail.
fdl(_).

De allra flesta moderna Prologer behöver inte längre den här syntaxen utan tillhandahåller ett predikat med högre ordning för att hantera detta.

nicer(X) :- forall(between(1,X,Y), print(Y)).

Detta är inte bara mycket lättare att läsa, utan om ett mål som kunde misslyckas användes istället för utskrift , skulle dess fel upptäckas korrekt och vidarebefordras - medan misslyckanden i målen i en felstyrd slinga förväxlas med det tvingade felet som driver slingan.

Visual Prolog har ett anpassat syntaktiskt socker för dessa slingor, i kombination med funktionspredikat (se nedan):

vploop(X) :- foreach Y = std::fromTo(1,X) do
                 console::write(X)
             end foreach.

Även om detta ser ut som ett krav för slinga följer det fortfarande Prolog-regler: i synnerhet är varje iteration av förhanden sitt eget räckvidd.

Funktionsstil Predikat

Traditionellt i Prolog skrivs "funktioner" (med en utgång och bundna ingångar) som vanliga predikat:

mangle(X,Y) :- Y is (X*5)+2.

Detta kan skapa svårigheten att om ett funktionsstilpredikat kallas flera gånger är det nödvändigt att "tusensköna kedja" tillfälliga variabler.

multimangle(X,Y) :- mangle(X,A), mangle(A,B), mangle(B,Y).

I de flesta Prologs, är det möjligt att undvika detta genom att skriva en alternativ infix operatör att använda i stället för is som expanderar uttryck inklusive den alternativa funktionen.

% Define the new infix operator
:- op(900, xfy, <-).

% Define our function in terms of the infix operator - note the cut to avoid
% the choice falling through
R <- mangle(X) :- R is (X*5)+2, !.

% To make the new operator compatible with is..
R <- X :-
    compound(X),            % If the input is a compound/function
    X =.. [OP, X2, X3],     % Deconstruct it
    R2 <- X2,               % Recurse to evaluate the arguments
    R3 <- X3,
    Expr =.. [OP, R2, R3],  % Rebuild a compound with the evaluated arguments
    R is Expr,              % And send it to is
    !.
R <- X :- R is X, !.        % If it's not a compound, just use is directly

Vi kan nu skriva:

multimangle(X,Y) :- X <- mangle(mangle(mangle(Y))).

Vissa moderna Prologer går dock längre och erbjuder en anpassad syntax för denna typ av predikat. I Visual Prolog till exempel:

mangle(X) = Y :- Y = ((X*5)+2).
multimangle(X,Y) :- Y = mangle(mangle(mangle(X))).

Observera att <- operatören och predikatet med funktionell stil fortfarande uppför sig som relationer - det är lagligt för dem att ha valpunkter och utföra flera föreningar. I det första exemplet förhindrar vi detta genom att använda nedskärningar. I Visual Prolog är det normalt att använda den funktionella syntaxen för relationer och valpunkter skapas på normalt sätt - till exempel målet X = (std::fromTo(1,10))*10 lyckas med bindningar X = 10 , X = 20, X = 30, X = 40, etc.

Flödes- / lägesdeklarationer

Vid programmering i Prolog är det inte alltid möjligt eller önskvärt att skapa predikat som förenar sig för varje möjlig kombination av parametrar. Exempelvis predikatet between(X,Y,Z) som uttrycker att Z är numeriskt mellan X och Y. Det implementeras enkelt i de fall där X, Y och Z alla är bundna (antingen Z är mellan X och Y eller det är inte), eller där X och Y är bundna och Z är fritt (Z förenar sig med alla siffror mellan X och Y, eller predikatet misslyckas om Y <X); men i andra fall, till exempel där X och Z är bundna och Y är fri, finns det potentiellt ett oändligt antal föreningar. Även om detta kan implementeras skulle det vanligtvis inte vara det.

Flödesdeklaration eller lägesdeklarationer tillåter en tydlig beskrivning av hur predikat uppträder när de kallas med olika kombinationer av bundna parametrar. När det gäller between skulle förklaringen vara:

%! between(+X,+Y,+Z) is semidet.
%! between(+X,+Y,-Z) is nondet. 

Varje rad specificerar ett potentiellt samtalsmönster för predikatet. Varje argument är dekorerat med + att indikera fall där det är bundet, eller - att indikera fall där det inte är (det finns också andra dekorationer tillgängliga för mer komplexa typer som tappar eller listor som kan vara delvis bundna). Nyckelordet efter är indikerar beteendet hos predikatet i det fallet, och kan vara något av följande:

  • det om predikatet alltid lyckas utan valpunkt. Till exempel add(+X,+Y,-Z) är det eftersom att lägga till två givna siffror X och Y kommer alltid att ha exakt ett svar.
  • semidet om predikatet antingen lyckas eller misslyckas, utan valpunkt. Som ovan är between(+X,+Y,+Z) semidet eftersom Z antingen är mellan X och Y eller så är det inte.
  • multi om predikatet alltid lyckas, men kan ha valpunkter (men kanske inte). Till exempel skulle factor(+X,-Y) vara multi eftersom ett nummer alltid har minst en faktor - sig själv - men kan ha mer.
  • nondet om predikatet kan lyckas med valpunkter, eller misslyckas. Till exempel är between(+X,+Y,-Z) nondet eftersom det kan finnas flera möjliga föreningar av Z till siffror mellan X och Y, eller om Y <X så finns det inga nummer mellan dem och predikatet misslyckas.

Flödes- / lägesdeklarationer kan också kombineras med argumentmärkning för att klargöra vilka termer som betyder eller med att skriva. Till exempel är between(+From:Int, +To:Int, +Mid:Int) is semidet .

I rena Prologs är flödes- och lägesdeklarationer valfria och används bara för att generera dokumentation, men de kan vara extremt användbara för att hjälpa programmerare att identifiera orsaken till inställningsfel.

I Mercury är flödes- och lägesdeklarationer (och typer) obligatoriska och valideras av kompilatorn. Syntaxen som används är som ovan.

I Visual Prolog är flödes- och lägesdeklarationer och typer också obligatoriska och syntaxen är annorlunda. Ovanstående förklaring skulle skrivas som:

between : (int From, int To, int Mid) determ (i,i,i) nondeterm (i,i,o).

Betydelsen är densamma som ovan, men med skillnaderna att:

  • Flödet / lägesdeklarationerna är separerade från typdeklarationerna (eftersom det antas att flöde / läge för ett enda predikat inte kommer att variera med typöverbelastning);
  • i och o används för + och - och matchas med parametrarna baserade på beställning;
  • Användade termer är olika. det blir procedure , semidet blir determ och nondet blir nondeterm ( multi är fortfarande multi ).


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow