A Mercury programozási nyelv

Állapotváltozók

A klózok használhatnak állapot változókat a sorozatokban szereplő közbülső értékek megnevezésének gyorsítására. Vagyis ahol az egyszerű szintaxissal az írnánk, hogy

  main(IO0, IO) :-
io.write_string("The answer is ", IO0, IO1),
io.write_int(calculate_answer(...), IO1, IO2),
io.nl(IO3, IO).

ott állapot változókat használva azt irhatjuk, hogy

  main(!IO) :-
io.write_string("The answer is ", !IO),
io.write_int(calculate_answer(...), !IO),
io.nl(!IO).

Az állapot változó jelölése !.X vagy !:X, amik rendre az X-el jelölt sorozat "aktuális" és a "következő" értékét jelentik. Az !X argumentum rövidítése a következő két állapotváltozó argumentumnak: !.X, !:X; vagyis a p(..., !X, ...) kiértékelése a p(..., !.X, !:X, ...) kifejezés kiértékelésével történik.

Minden klózon belül megtörténik az állapot változók átalakítása szokásos logikai változók sorozatává. A termekre vonatkozó szintaktikai konverziót a következőkben definiált, fogalmi transform függvény segítségével írjuk le.

Az átalakítást egyszer alkalmazzuk minden X állapot változóra, mely során új változók friss kerülnek bevezetésre, ezeket ThisX-nek és NextX-nek nevezzük el.

A substitute(Term, X, ThisX, NextX) kifejezés a Term kifejezés olyan másolatát jelenti, ahol !.X szabad előfordulásait ThisX-re, és a !:X előfordulásait NextX-re cseréltük (a szabad előfordulás nem kötött klózfej, lambda kifejezés feje vagy explicit kvantálás által.)

Az állapot változók követik a speciális látahtósági szabályokat. Az X állapot változót explicit be kell vezetni vagy a klóz vagy a lambda kifejezés fejében (ebben az esetben előfordulhat !.X, vagy !:X, vagy mindkettő) vagy egy explicit kvantálással (ebben az esetben csak !X-ként szerepelhet.) Egy lambda vagy egy if-then-else kifejezésben előforduló X állapot változóra a hatáskörében csak a !.X formában hivatkozhatunk.

Példának okáért a következő cél egy if-then-else kifejezést alkalmaz:

  p((if q(!X), r(!X) then A else B), !X)

ez nem érvényes forma, mivel implicit utalást tartalmaz !:X -ra az if-then-else kifejezés feltételében. Viszont a

  p((if some[!X] (q(!X), r(!X)) then A else B), !X)

alak elfogadható, mivel az Xállapotváltozó lokális hatóköre kiterjed az if-then-else kifejezés feltételére és a then-célra, ezért a !:X előfordulhat bennük.

A lambda kifejezésekben levő állapot változókat illetően három megszorítás van érvényben: először is, !X nem érvényes függvényeredmény,mivel két argumentumből áll egy helyett; másodszor, !X nem jelenhet meg paraméter termként egy lambda kifejezés fejében, mivel nincs szintaxis arra, hogy megadjuk a két paraméter módját; harmadszor, !X nem fodulhat elő egy függvény applikációban argumentumként, mivel ennek nincs értelme az állapot változók és függvények szokásoson interpretációját tekintve.


Head :- Body

	  transform((Head :- Body), X, ThisX, NextX) =
substitute(Head, X, ThisX, NextX) :- transform(Body, X, ThisX, NextX)

Head --> Body

	  transform((Head --> Body), X, ThisX, NextX) =
substitute(Head, X, ThisX, NextX) :- transform(Body, X, ThisX, NextX)

Goal1, Goal2

	  transform((Goal1, Goal2), X, ThisX, NextX) =
transform(Goal1, X, ThisX, TmpX), transform(Goal2, X, TmpX, NextX)
ahol TmpX friss változó.
Goal1 ; Goal2

	  transform((Goal1 ; Goal2), X, ThisX, NextX) =
transform(Goal1, X, ThisX, NextX) ; transform(Goal2, X, ThisX, NextX)

not Goal

\+ Goal
Negáció. A két különböző szintaxis ugyanazt a szemantikát takarja.
	  transform((not Goal), X, ThisX, NextX) =
not transform(Goal1, X, ThisX, DummyX), NextX = ThisX
a DummyX friss változókra.
if Goal1 then Goal2 else Goal3

Goal1 -> Goal2 ; Goal3
Egy if-then-else. A két különböző szintaxisnak ugyanaz a szemantikája.
	  transform((if Goal1 then Goal2 else Goal3), X, ThisX, NextX) =
if transform(Goal1, X, ThisX, TmpX) then transform(Goal2, X, TmpX, NextX)
else transform(Goal3, X, ThisX, NextX)
a TmpX friss változókra.
Goal1 => Goal2

Goal2 <= Goal1
Egy implikáció. A két különböző szintaxisnak ugyanaz a szemantikája.
	  transform((Goal1 => Goal2), X, ThisX, NextX) =
transform(Goal1, X, ThisX, TmpX) => transform(Goal2, X, TmpX, NextX),
NextX = ThisX
a TmpX friss változókra.
all Vars Goal

	  transform((all Vars Goal), X, ThisX, NextX) =
all Vars transform(Goal, X, ThisX, DummyX), NextX = ThisX
a DummyX friss változókra.
some Vars Goal

	  transform((some Vars Goal), X, ThisX, NextX) =
some Vars transform(Goal, X, ThisX, NextX)

Hívás_vagy_Egyesítés
Ha !:X nem fordul elő a Hívás_vagy_egyesítés kifejezésben, akkor
	  transform(Call_or_Unification, X, ThisX, NextX) =
substitute(Call_or_Unification, X, ThisX, NextX), NextX = ThisX
Ha !:X előfordul a Hívás_vagy_egyesítés kifejezésben, akkor
	  transform(Call_or_Unification, X, ThisX, NextX) =
substitute(Call_or_Unification, X, ThisX, NextX)

Ez az átalakítás változóegyesítések láncolatainak megjelenését eredményezheti, amik egyébként nem játszanak szerepek a definícióban. Ilyen láncok eltávolításra kerülnek.

A következő kódrészletek az állapotváltozók helyes használatának szintaxisát illusztálják.


Az IO állapotok felfűzése

	  main(!IO) :-
io.write_string("The 100th prime is ", !IO),
X = prime(100),
io.write_int(X, !IO),
io.nl(!IO).

Akkumulátorok kezelése (1)

	  foldl2(_, [], !A, !B).

foldl2(P, [X | Xs], !A, !B) :-
P(X, !A, !B),
foldl2(P, Xs, !A, !B).

Akkumulátorok kezelése (2)

	  iterate_while2(P, F, !A, !B) :-
( if P(!.A, !.B) then
F(!A, !B),
iterate_while2(P, F, !A, !B)
else
true
).