Kifejezés kiértékelése

8.1. Kérdés.
Adott a következő értékdeklaráció:

val x = List.filter (fn true => false | _ =>true)
                          [3.0>4.0, 4.7>2.4, ~3.0<=0.4]
erre az mosml azt írja ki, hogy x = [false]. De miért? Mit csinál a List.filter-ben megadott feltétel?

8.1. Válasz.
Oktató: A teljes kifejezés a listában lévő igaz értékű elemeket eldobja, a hamis értékűeket kigyűjti.

A predikátum (List.filter első argumentuma) éppen ezt fogalmazza meg: ha az elem true, eldobja, ha false (pontosabban bármi más, vö. _ => true, de most csak true lehet), akkor kigyűjti.

A pontosság kedvéért: a List.filter első argumentumaként nem egy feltételt, azaz egy bool típusú kifejezést, hanem egy predikátumot, azaz egy bool típusú eredményt adó függvényt vár.

8.2. Kérdés.
Az SML kisházi megoldása közben a honlapon megadott példák közül mind lefutott, az utolsót kivéve, amely a következő hibaüzenetet adta:

- dadogo 123456789;
! Uncaught exception
! Overflow
Feltételezem, hogy az mosml ennél nagyobb int típusú számokat is képes kezelni, ezért nem értem a dolgot.

8.2. Válasz.
Oktató: 123456789 még valóban belefér az int tartományba. De ha megszorozza pl. 9-cel, ezt kapja:

- 123456789 * 9;
! Uncaught exception:
! Overflow
8-cal még éppen megszorozhatja. Feltehetően olyan műveletet végez a számmal, amellyel kilép az int tartományból.

8.3. Kérdés.
A következő függvények betöltésénel a Warning: calling polyEqual hibaüzenetet kapom az smlnj-től. Az iránt érdeklődnék, hogy ez pontosan mit jelent.

open List;
fun dadoge [] = true
  | dadoge a = if hd a = last(take(a, length a div 2 + 1))
                    andalso parose a = true
               then dadoge(drop(take(a, length a div 2), 1) @
                           drop(drop(a, length a div 2), 1))
               else false
and parose a = length a mod 2 = 0;

8.3. Válasz.
Oktató: Lássuk akkor pontosabban a figyelmeztetést (csak figyelmeztet, nem hibát jelez!) smlnj-vel:
... Warning: calling polyEqual
val dadoge = fn : ''a list -> bool
val parose = fn : ''a list -> bool
majd mosml-lel:

> val ''a dadoge = fn : ''a list -> bool
  val ''a parose = fn : ''a list -> bool
Az smlnj tehát akkor is figyelmeztet, ha valaki az egyenlőségvizsgálatot politípusú értékeken végezteti el. A következő nagyon egyszerű esetben ugyanez a helyzet:

- fun eq (x,y) = x=y;
... Warning: calling polyEqual
val eq = fn : ''a * ''a -> bool
Ha típusmegszorítást használunk, mindent rendben lévőnek talál:

- fun eq (x,y:int) = x=y;
val eq = fn : int * int -> bool
Ezzel szemben egyáltalán nem engedi meg a következőt:

- fun eq (x,y:real) = x=y;
... Error: operator and operand don't agree
           [equality type required]
  operator domain: ''Z * ''Z
  operand:         ''Z * real
  in expression:
    x = y
ui. real típusú értékek egyenlősége az = operátorral az smlnj használatakor nem vizsgálható.

Helyette a Real.== vagy a Real.?= függvény használható. Pl.

fun eq (x,y) = Real.==(x,y);
val eq = fn : real * real -> bool
A Real.== és a Real.?= műveletek kielégítik az IEEE standard 754-1985 szerint előírt specifikációt (lásd: <http://www.standardml.org/Basis/real.html#REAL:SIG:SPEC>).

Most már az is érthető, hogy politípusú értékek egyenlőségvizsgálatakor miért figyelmeztet az smlnj: előfordulhat, hogy a létrehozott függvény a későbbiekben olyan értékek egyenlőségét akarja megvizsgálni, amelyekre az egyenlőségvizsgálat nem végezhető el.

Az mosml-ben a Real.== ekvivalens az = operátorral, ha az utóbbi operandusai real típusúak, a Real.?= eredménye pedig mindig false, mert az IEEE standard szerinti számábrázolások az mosml-ből hiányoznak.

8.4. Kérdés.
Ha egy függvényben használok egy belső függvényt (let fun...), amelyben az egyenlőségjel jobb oldalán (azaz a belső függvény törzsében) feltételvizsgálatra van szükségem, akkor ennek else ágában hogyan léphetek ki a belső függvényből?

8.4. Válasz.
Oktató: Ugyanúgy, mint a then ágából: a megfelelő típusú értékkel.

Hangsúlyozom, hogy az SML-ben az if... then... else: kifejezés, mégpedig egy feltételtől függően így vagy úgy kiértékelendő kifejezés. Ha az else ágban nincs rekurzív hívás, az esetleges műveletek elvégzése után a függvény kiértékelése is befejeződik, azaz az SML visszatér a függvényhívásból.

A kérdező folytatja: Ha van több lehetőség is, melyik mit jelent pontosan ?

Oktató: Ezt nem értem: ha az if... then... else... if...else ... szerkezetre gondol, akkor a kiértékelés mindig az if után álló predikátumtól függ. Ha a függvény klózaira gondol, akkor pedig a függvényfejben megadott mintáktól.

Figyelem: mind a then és else-ágakban, mind a függvény klózaiban álló kifejezések függetlenek egymástól!

A kérdező folytatja: Valójában a belső függvényből való kilépéssel együtt a külső függvényt is be szeretném fejezni, és a belsőből való kilépésig kiszámított eredményeket visszadni. Remélem, nagyjából követhető volt a probléma leírása, és van ötletetek a megoldására.

Oktató: Ennek semmi akadálya: ha a belső függvény visszatérési értékével egyúttal az őt meghívó függvényből is kilép, akkor a külső függvény által visszaadott érték meg fog egyezni a belső függvény által visszaadott értékkel (a belső függvény visszatérési értékével).

Abból, amit leírt, azért nem nagyon értem, hogy mi a problémája. Ha esetleg arra gondol, hogy valamilyen return v vagy exit if parancs van-e az SML-ben, akkor erre nem a válasz, mert nincs rájuk szükség. Ha megnézi az előadásokon bemutatott példákat, sok mintát talál.

8.5. Kérdés.
Adott egy programrészlet:

fun megy m n =
  let
    fun megy2 sor1 oszlop1 =
     if sor1 = n andalso oszlop1 > m
     then   (* legalján vagyunk, visszatérünk valamivel *)
          [[(1,1,1,1)]]
     else if sor1 <= n andalso oszlop1 > m
     then   (* sor végén vagyunk *)
          megy2 sor1+1 1
     else   (* sorban vagyunk *)
          megy2 sor1 oszlop1+1
  in
      megy2 1 1
  end;
A lényege az, hogy végiglépkedek egy mátrixon bal fentről jobbra le. Fordításánal ezt a hibát kapom:

! Toplevel input:
!           megy2 sor1+1 1
!           ^
! Type clash: expression of type
!   'a -> (int * int * int * int) list list
! cannot have type
!   (int * int * int * int) list list
De miért? Miért akarja az

'a -> (int * int * int * int) list list
típust adni neki?

8.5. Válasz.
Oktató: A precedenciákkal, pontosabban a függvényalkalmazás móhóságával van baj: a függvényeknek argumentumként átadott összetett kifejezéseket zárójelbe kell tenni, azaz:

...
     then   (* sor végén vagyunk *)
          megy2 (sor1+1) 1
     else   (* sorban vagyunk *)
          megy2 sor1 (oszlop1+1)
...

8.6. Monológ.
Hallgató: Megoldottam a listára küldött példát mind mohó, mind a lusta módszerrel. Az eredmény egyezik, csak nem vagyok biztos benne, hogy minden lépés, ill. azok sorrendje helyes-e. A hibákat, észrevételeket légyszi küldjétek vissza! A függvény:

fun  f g [] = []
  | f g (x::xs) = map g x :: f g xs;
Egy alkalmazása lusta kiértékeléssel:

f chr [[2*5, 7 div 3], [round 8.0], []] =
   chr [2*5, 7 div 3] :: f chr [[round 8.0], []] =
   chr [2*5, 7 div 3] :: chr [round 8.0] :: f chr [] =
   chr [2*5, 7 div 3] :: chr [round 8.0] :: [] =
   [chr 2*5, chr 7 div 3] :: chr [round 8.0] :: [] =
   [chr 2*5, chr 7 div 3] :: [chr round 8.0] :: [] =
   [chr 10, chr 7 div 3] :: [chr round 8.0] :: [] =
   [chr 10, chr 2] :: [chr round 8.0] :: [] =
   [chr 10, chr 2] :: [chr round 8.0] :: [] =
   [#"\n", chr 2] :: [chr round 8.0] :: [] =
   [#"\n", #"\^B"] :: [chr round 8.0] :: [] =
   [#"\n", #"\^B"] :: [chr 8] :: [] =
   [#"\n", #"\^B"] :: [#"\b"] :: [] =
   [#"\n", #"\^B"] :: [#"\b"] =
   [[#"\n", #"\^B"], [#"\b"]]
Ugyanez mohó kiértékeléssel:

f chr [[2*5, 7 div 3], [round 8.0], []] =
   f chr [[10, 7 div 3], [round 8.0], []] =
   f chr [[10, 2], [round 8.0], []] =
   f chr [[10, 2], [8], []] =
   f chr [[10, 2], [8], []] =
   [chr 10,2] :: f chr [[8], []] =
   [#"\n", 2] :: f chr [[8], []] =
   [#"\n",chr 2] :: f chr [[8], []] =
   [#"\n",#"^B"] :: f chr [[8], []] =
   [#"\n",#"\^B"] :: chr [8], f chr [] =
   [#"\n",#"\^B"] :: [#"\b"] :: f chr [] =
   [#"\n",#"\^B"] :: [#"\b"] :: [] =
   [#"\n",#"\^B"] :: [#"\b"] :: [] =
   [#"\n",#"\^B"] :: [#"\b"] =
   [[#"\n",#"\^B"],[#"\b"]]


Deklaratív programozás - FP-GYIK
2005. március 1.