Függvény, rekurzió

9.1. Kérdés.
Lenne egy egyszerű kérdésem. Egy függvény végeredményét hogy lehet negálni?

9.1. Válasz.
Oktató: SML-ben a not függvény alkalmazásával.

9.2. Kérdés.
Van valakinek 5lete, hogy lehetne egy eljárás több klózában ugyanazokat a lokális változókat használni?

Háromféleképp próbáltam, de nem ment (egyszer az end-re, aztán a |-ra (vonásra) mondott syntax error-t). Fiktív példákkal:

fun Q (A:As,[]) = bla
  | Q (A:As,B:Bs) =
         let A = 1+1
         in
            if A = blablabla ...
         end
  | Q ([],B:Bs) =
         let A = 1+1
         in
            if A = blablabla ...
         end
Az 1. példában a legelső end-nel volt gond.

fun Q (A:As,[]) = bla
  | Q (A:As,B:Bs) =
         let A = 1+1
         in
            if A = blablabla ...
  | Q ([],B:Bs) =
            if A = blablabla ...
         end
A 2. példában a második |-sal volt gond.

let A = 1+1
  in
     fun Q(A:As,[]) = bla
       | Q(A:As,B:Bs) =
            if A = blablabla ...
       | Q([],B:Bs) =
            if A = blablabla ...
end
A 3. példában a fun-nal volt gond.

9.2. Válasz.
Oktató: Látszólag tényleg ezekkel volt a gond, a szintaxisellenőrző legalábbis ezeknél vette észre, hogy valami hézag van. A nagyon elemi hibák azonban máshol vannak.

Hibák az 1. példában:

A 2. példában is felsoroltak mellett még egy hiba van:

A 3. példában a korábbiakhoz képest nincs új hiba, ám a külső let-kifejezésben hiába deklarálja (deklarálná, ha nem lenne hibás a deklaráció!) a lokális A nevet, azt a Q függvény első és második klózában bevezett A paraméterek, amelyek maguk is egymástól független dolgokat jelölnek, elfedik (vö. láthatóság és érvényességi kör). Egyedül a harmadik klózban látszik a külső let-kifejezésben bevezett A név, de tartok tőle, hogy nem ezt akarta elérni.

E hosszú történet talán legfontosabb tanulsága, hogy bárkinek, mielőtt programírásba fogna, érdemes megismerkednie az SML-kifejezések elemi szintaxisával. Ajánlom mindenkinek a figyelmébe az SML szintaxisát ismertető fejezetet a jegyzetben.

9.3. Kérdés.
Az lenne a kérdésem, hogy ha SML-ben két függvény egymást hívja, akkor mit kell csinálni, hogy a fordító elfogadja. Ugye az egyik mindenképp olyan függvényt hív meg, amelyet még nem definiáltunk. Meg lehet valahogy mondani a fordítónak, hogy ilyen nevű függvényt definiálni fogunk később?

9.3. Válasz.
Oktató: Kölcsönösen rekurzív függvényeket és más egymástól függő értékeket az ún. egyidejű deklarációval hozhatunk létre. Erre való az and kulcsszó. Pl.

fun odd  0 = false
  | odd  n = even(n-1)
and even 0 = true
  | even n = odd(n-1)
Az and más deklarációsorozatokban is használható, nemcsak kölcsönösen rekurzív vagy egyéb, egymástól függő esetekben. Pl.

val harom = 3
and negy  = 4
and ot    = 5
fun f1 x  = x * 2
and f2 x  = x div 2
abban különbözik a

val harom = 3
val negy  = 4
val ot    = 5
fun f1 x = x * 2
fun f2 x = x div 2
deklarációsorozattól, hogy az első sorozatban az első hármat, majd pedig a második kettőt egyszerre értékeli ki az mosml, míg a második sorozatban minden sort külön-külön, azaz szekvenciálisan értékel ki. Ha kiírjuk a pontosvesszőket (amit egyébként deklarációk között nem kötelező kitenni), akkor jobban látszik a különbség, az and elé ugyanis nem szabad pontosvesszőt tenni, ha kitesszük, hibajelzést kapunk:

val harom = 3
and negy  = 4
and ot    = 5;
fun f1 x  = x * 2
and f2 x  = x div 2;
és

val harom = 3;
val negy  = 4;
val ot    = 5;
fun f1 x = x * 2;
fun f2 x = x div 2;
Az egyidejű deklaráció felhasználható kötések felcserélésére is, pl. a

val alma = "szeretem"
val korte = "nemszeretem"
vagy a

val alma = "szeretem" and korte = "nemszeretem"
deklaráció után a

val alma = korte and korte = alma
deklaráció megcseréli a két kötést, míg a

val alma = korte
val korte = alma
deklaráció a szekvenciális kiértékelés miatt nem a remélt eredményhez vezet.

9.4. Kérdés.
Hogyan oldható meg úgy egy függvénydeklaráció, hogy ne kelljen különválasztanom a nil esetet és a listaátadás esetét? Azaz:

fun valami (nil) = ...
  | valami (l)   = ...
így jó, de nekem az kellene, hogy

fun valami (l) = ...
esetén is működjön, és l értéke legyen nil, ha arról van szó. Ha ilyent írok be, akkor mindig kiírja, hogy nem fedem le az összes esetet, pedig én szeretném. :)

9.4. Válasz.
Oktató: Ez valahogy nem stimmel. Az l mint minta mindenre illeszkedik, tehát nem okozhat figyelmeztetést. Annak valami más lehet az oka. Megjegyzés: a zárójel a nil és az l körül felesleges (persze nem hiba).

Kérés: ha "hogyan kell megoldani, hogy..." vagy "miért az a válasz, hogy..." jellegű kérdésük van, kérjük, mellékeljék a megfelelő (rövid, de a kérdéses viselkedést produkáló) programrészletet és az értelmező válaszát, ui. csak akkor tudjuk pontosan megmondani, hogy mi lehet a gond. Ezek nélkül legfeljebb találgathatunk, vagy széttárhatjuk a kezünket, mint most.


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