A Pizza nyelv

Utasítások, vezérlési szerkezetek

A vezérlési szerkezetek nagy része ugyanaz mint Java-ban, pár újdonságtól eltekintve, amik alább láthatóak.

Tail call recursion (continue, goto)

Egy kis "újdonság", hogy megjelent a goto kulcsszó ;). Ha nagyon mély rekurziót írunk, előfordulhat, hogy a programunk leáll StackOverflowException-nel. Ez azért van, mert minden visszatérési cím, lokális változó és függvényparaméter a veremben tárolódik. Egy speciális esetben ez az intenzív veremhasználat elkerülhető. Azt az esetet, amikor a rekurzív függvény a törzse végén hívja meg saját magát "tail call recursion"-nek hívjuk. Ebben az esetben jelezni tudjuk a fordítónak, hogy olyan kódot generáljon, ami nem használja a vermet. Ezt a függvény előtti continue kulcsszóval jelezzük, a rekurzív hívást a függvénytörzs végén pedig a goto kulcsszóval kell jeleznünk.

continue int nagyonRekurzív( int mélység ) { if ( mélység == 0 ) { System.out.println("Húúú -- végre itt vagyok!"); return -1; } else return goto nagyonRekurzív( mélység - 1 ); }

Mintaillesztés (switch)

Hasznos kiegészítés még az osztály típusára vonatkozó mintaillesztés. A Pizza a Java switch vezérlőelemét terjeszti ki, hogy ezt a funkcionalitást megvalósítsa. Vegyük az alábbi példát:

Tree<String> t = ... ; String s; if ( t instanceof Branch ) { s = (Branch)t.elem; } else { s = "(empty)"; }

Ehelyett használhatunk mintaillesztést, a switch és case kulcsszavakkal:

Tree<String> t = ... ; String s; switch( t ) { case Empty: s = "(empty)"; case Branch( A elem, Tree<A> l, Tree<A> r ): s = elem; }

Ez sokkal átláthatóbb és jobban kifejezi, amit akarunk. Mindemellett ha az adott case ágban az objektum rendelkezik a megadott szignatúrájú konstruktorral, akkor az adott ághoz tartozó kódban elérhetővé válnak a formális paramétereken keresztül az objektum ezen adattagjai. Az egyezés pozíció alapján történik, tehát a szignatúrának egyeznie kell.

A joker paraméter a mintában az aláhúzás. Ezt akkor használhatjuk, ha az adott ágban egy paramétert nem szeretnénk használni:

switch( t ) { case Branch( _, Tree<A> r, _ ): return r.elem; }

A változók több szintjén is lehet mintailleszteni, ezt hívják mély mintaillesztésnek (deep pattern matching). A következő példán szemléltetjük ezt. A mapPairs két lista elemeit veszi sorra és a paraméterül kapott függvénnyel csinál belőlük egy új elemet amit felfűz egy harmadik listára. A végén ezt adja vissza.

class Pair<A, B> { case Pair( A a, B b ); } class List<A> { case Nil; case Cons( A z, List zs ); } static <A, B> List<C> mapPairs( (A, B) -> C f, List<A> xs, List<B> ys ) { List<C> result; switch ( Pair.Pair( xs, ys ) ) { case Pair(List.Nil, _): case Pair(_, List.Nil): result = List.Nil; break; case Pair(List.Cons(A x, List<A> xs1), List.Cons(B y, List<B> ys1)): result = List.Cons(f(x, y), mapPairs(f, xs1, ys1)); break; case _: System.out.println("case 2"); break; } return result; }