Az ML programozási nyelv

Függvények

Függvények definiálása

Az ML-ben a függvények maguk is értékek. Mégpedig egyváltozósak, de ez a változó lehet egy rendezett n-es vagy akár egy leképezés(függvény) is. A képe pedig n-es vagy függvény. Az előzőek miatt a függvényeket a Curry féle jelölésmódban kell definiálni és használni.

Egy függvényt a fun kulcsszó segítségével definiálhatunk.

fun < fun_name > < parameter > = < expression > [ | < fun_name > < parameter > = < expression > ];
vagy fun nélkül :
val < fun_name > = fn < parameter > => < expression> [ | < parameter > => < expression > ];

Példák:

fun double x = 2*x; fun inc x = x+1; fun adda s = s ^ "a"; fun fact(0)=0 |fact(n)=fact(n-1);

Ezeket a függvényeket egyszerűen a beírásukkal definiáltuk. Ahhoz, hogy egy függvényt végrehajtsunk, egyszerűen adjuk meg a nevét, utána pedig az aktuális paramétereket. Például:

double 6; inc 100; adda "tub";

A fenti kifejezésekre a rendszernek a következő értékeket kell visszaadnia: 12 : int, 101 : int, "tuba" : string.

Definiáljuk a "double" egyszerű függvényt a következőképpen:

fun double x = 2 * x;

A függvényt kipróbálhatjuk, úgy, hogy egy kiértékelhető kifejezést teszünk az argumentumába, pl.:

double 3;

A "times4" függvényt elkészíthetjük a "double" kétszeri alkalmazásával, ezt hívjuk függvénykompozíciónak:

fun times4 x = double(double x);

A több mint egy bemenettel rendelkező függvényeket rendezett n -esekkel (tuple) definiálhatjuk. Például definiálhatjuk az "aveI" -t és az "aveR" -t a következőképpen:

fun aveI(x,y) = (x+y) div 2; fun aveR(x,y) = (x+y)/2.0;

Megfigyelhetjük, hogy az ML maga "találja ki" az egyes függények típusát.

aveR(3.1 , 3.5); aveI(31, 35);

Curry függvények

Egy egynél több bemenettel rendelkező függvényt implementálhatunk rendezett n -esek (tuple) segítségével vagy ún. "curry" függvényekkel (H. B. Curry után). Nézzünk egy olyan függvényt, amely összead két egész számot. Ez rendezett n -esekkel megvalósítva:

fun add(x,y)= x+y : int; val add = fn int * int -> nt

Ennek a függvénynek a bemenete egy int * int rendezett pár. A Curry féle verziója ennek a függvénynek zárójelek vagy vesszők nélkül van definiálva:

fun add x y = x+y : int; val add = fn : int -> int -> int

Ennek a függvénynek a típusa int -> (int -> int). Tehát ez egy olyan függvény, amely egy egész számból egy olyan függvényt csinál amely egészekből egészeket csinál. A függvény argumentumait zárójelek nélkül adhatjuk meg:

add 2 3; it = 5 : int

Csak egy argumentumot megadva az eredmény a függvény "parciális kiértékelése lesz". Például meghívva az "add" függvényt csak a "2" paraméterrel az eredmény egy olyan függvény lesz, amely kettőt ad a bemenetéhez.

add 2; it = fn int-> int it 3; it = 5 : int

A Curry függvények hasznosak lehetnek olyan esetekben, amikor függvényeket adunk át egy másik függvény paraméterének.

Erre egy egyszerű példát mutatunk. Adott egy f függvény, a twice f-et alkalmazza paraméterként.

val twice = fn f => fn x => f (f x)

Az egész szám következő száma függvény pedig:

fn x => x+1

Ez haszhálható a twice paramétereként.

val addtwo = twice (fn x => x+1)

A twice egy egyszerű függvény (curry függvény speciális esete), melynek paramétere saját maga. Most definiáljuk az általános iterációs függvényt. Ez a curry függvény egy curry függvényt ad eredményként.

iter n f x = fn(x) = f (f ( ... f (x) ...))

Legegyszerűbb esetben f0 = id, az identitás függvény. Ha n pozitív

fn(x) = f(fn_1(x)).

Most implemetáljuk az iter függvényt Standard ML-ben.

val rec iter = fn 0 => (fn f => fn x => x) | n => (fn f => fn x => f (iter (n-1) f x))

Megvizsgálunk két magasabb rendű Standard ML függvényt, melyek közül az egyik egy függvényt transzformál az ő curry alakjába, a másik egy curry függvényt tuple-alakba.

val curry = fn f => fn x => fn y => f (x, y) val uncurry = fn f => fn (x, y) => f x y

Ha x és y értékek, f és g függvények, akkor mindig létezik:

(curry f) x y = f (x, y) (uncurry g) (x, y) = g x y

Anonymous függvények

A függvényeket név nélkül is definiálhatjuk, a következő szintaxissal:
fn < parameters > => < expression >

Példa:

fn x => 2*x; it = fn : int -> int it 14; 28 : int

Ez nagyon hasznos tud lenni, ha magasabbrendű függvényeket használunk, mint a map:

map (fn x=> 2*x) [2,3,4];

Matematikai és stringkezelő függvények

Az ML -ben megtalálhatóak a szokásos matematikai és stringkezelő függvények, például:

+ egész vagy valós összeadás
- egész vagy valós kivonás
* egész vagy valós szorzás
/ valós osztás
div egész osztás pl. 27 div 10 is 2
mod maradék pl. 27 mod 10 is 7
^ string konkatenáció pl. "cub"^"a"

Ezek mind infix operátorok, tehát két argumentum között jelennek meg.

Előre definiált függvények

Áttekintésképpen most az ML előre definiált függvényei következnek:

Direkt szorzat

( * ) ::= ( > < ) .

Aritmetikai függvények

negation::int->int="~" negation::real->real="~" max::int*int->int min::int*int->int abs::int->int abs::real->real real::int->real truncate::real->int ceiling::real->int floor::real->int sin::real->real cos::real->real arctan::real->real ln::real<>->real (* parciális függvény, nulla vagy kisebb számokra nincs definiálva *) sqrt::real<>->real (* parciális függvény, nulla vagy kisebb számokra nincs definiálva *) exp::real->real chr::int->string |-chr= /ord. (* --- chr az ord inverze. *) makestring::int|real->string print::int|real->unit

Összehasonlító függvények

=:: int*int->bool   (* egyenlő. *) <> :: int*int->bool (* nem egyenlő. *) < :: int*int->bool > :: int*int->bool <= :: int*int->bool >= :: int*int->bool

Stringkezelő függvények

A stringben szereplő karakterek meg vannak számozva 0, 1, 2, ... explode::string->string list (* minden karakter a (_)-ben a megadott lista elemévé válik. *) implode::string list->string (* összefűzi a (_) elemeit. *) (IE): implode o explode (* Id. *) (EI): explode o implode (* Id. *) size::string->int (* a stringben lévő karakterek száma *) substring::string*int*int->string (* (3rd) karakterek amik a (2nd) -tól kezdődnek a (1st) stringben. *) ord::string->int ordof::string*int->int print::string->unit

Boolean

andalso::bool*bool->bool orelse::bool*bool->>bool not::bool->bool makestring::bool->string print::bool->unit

Listakezelő függvények

hd::a' list -> a' for type a' tl::a' list->a' list for type a' nth::'a list*int->a' nthtail::'a list*int->a' list null::'a list->bool length::'a list->int rev::'a list->'a list map::('a -> 'b) -> 'a list -> 'b list for 'a and 'b types app, revapp:: ('a->'b)-> 'a list -> 'b list fold, revfold:: (('a * 'b)->'b) ->'a list ->'b -> 'b exists::('a->bool)->'a list-> bool

Tömbök

Mindenképpen meg kell hívni:

open Array;

array::int*'a -> 'a array sub::'a array*int->'a update::'a array * int * 'a -> 'a array length::'a array->int arrayoflist::'a list -> 'a array

Referenciák

dereference::="!"::'a ref -> 'a. assignment::=":="::'a ref*'a->unit (*az értékadás infix művelet!*) inc::int ref->unit dec::int ref->unit

Referenciák jelentősége miatt egy kicsit részletezem

Létezik referencia minta, lekérdezhető műveletekben kényelmesen helyettesíti a !-t:

fun += (_, ref 0) = () | += (a, ref b) = a := !a +

A while felt do kif kifejezés a hagyományos ciklust írhatjuk le:

val i = ref 1 while !i < 10 do (print (Int.toString (!i)); i := !i + 1)

Rejtett állapot:

Belső állapottal rendelkező objektum létrehozására mutat példát ez a fv:

fun counter () = let val c = ref 0 fun t () = (c := !c + 1; c) fun r () = (c := 0) in { tick = t, reset = r } end

Az objektum így használható:

val cnt1 = counter () val cnt2 = counter () #tick cnt1 () (* = 1 *) #tick cnt2 () (* = 1 *) #reset cnt1 () #tick cnt2 () (* = 2 *) #tick cnt1 () (* = 1 *)

Fontos, hogy minden példánynak saját állapota van, nem pedig közös az összesnek!

Bit Stringek

andb,orb,xorb::int*int->int lshift,rshift::int*int->int.

Byte tömbök

array::int*int->bytearray (*sub, update, length mint a tömböknél.*) extract::bytearray*int*int->string

fold, revfold.
app,revapp.

UNIX alapú függvények

use::string->unit (* betölti a (_) nevű UNIX file -t az aktuális könyvtárból.*) execute::string*string list->instream*outstream (*végrehajtja az első argumentumban megadott programot a második paraméterben szereplő argumentumokkal.*)