A Concurrent Clean 2.3 programozási nyelv

Alprogramok, függvények

Terminológia

A Cleanben a függvényeknek nagyon fontos szerepe van. Programvégrehajtási szempontból azonban teljesen mást értünk alatta, mint a szokásos imperatív nyelvekben: a függvény meghívása nem a visszatérési címnek és a paramétereknek a verembe írását és szubrutinhívást jelent, hanem a függvénynek megfelelő gráfújraírási szabály alkalmazását a programgráfra.

Paraméterezés

A függvényalkalmazás jelöléseként egyszerűen a függvény neve után írjuk a paramétereket, szóközökkel elválasztva. A függvénynek mindig van egy darab visszatérési értéke (eljárás tehát nem létezik), ez az eredmény lesz behelyettesítve a programgráfban a függvényalkalmazás helyére. A paraméterek bemenetként szolgálnak, illetve részei lehetnek a felépített eredménygráfnak. Az információátadási irány tehát rögzített, nem szabályozható. Mindezt már több példán keresztül láttuk.

A paraméterátadás cím szerinti, ha az eredményben hivatkozunk formális paraméterre, akkor az annak megfelelő aktuális paraméter-struktúrára fog hivatkozni az eredménygráf. A formális paraméter értékét megváltoztatni azonban nem lehet: a normális funkcionális nyelvi eszköztárban nincs is benne a felülírásos módosítás. Az egyedi típusú értékekre a Cleanben ez mégis lehetséges, ezekre viszont — definíció szerint — nem létezhet többszörös hivatkozás.

Ha azt szeretnénk, hogy a függvényünk több értéket adjon vissza, akkor úgy deklaráljuk, hogy ezen értékek n-ese legyen az eredmény típusa — ezt is láttuk már.

A formális paramétereknek alapértelmezett érték nem adható meg. A hiányos paraméterlistájú függvényalkalmazásnak más jelentése van: az egy részleges függvényalkalmazás, azaz megfelel egy olyan függvénynek, amelynek paraméterei az előbbi hiányzó paraméterek. Ez a függvény — ha alkalmazzuk az ő paramétereire — ugyanazt az eredményt adja vissza, mint az eredeti függvény, ha valamennyi, most már meglevő paraméterre is alkalmaztuk volna.

Szemléltetésül vegyük a (*) függvényt, amelyet két egész számra alkalmazva azok szorzatát kapjuk vissza: (*) 3 5. Ha azonban valahol (*) 3-at írunk, az a 3-mal való szorzás függvényét jelenti. Alkalmazhatjuk ezt a függvényt például az 5-re, így: ((*) 3) 5, bár ez egyelőre nem túl hasznos, mert ugyanazt jelenti, mint a (*) 3 5. De felhasználhatjuk más olyan helyen is, ahová egy egészről egészre képező függvényt kell írnunk:

module curried // curried application import StdEnv square:: Int -> Int square x = x * x Start:: ([Int],[Int]) Start = (map square [1,2,3,4], map ((*) 3) [1,2,3,4])

A fenti program egyben példa arra is, hogy függvény paramétere bármilyen, így függvény típusú is lehet. A függvény típusának meghatározásakor nem szükséges konkrét típusokat megadni, helyettük típusváltozókat is használhatunk; ezekre opcionálisan kikötések is tehetők (lásd hamarosan), így polimorfikus függvényeket definiálhatunk. Típust is át lehet adni paraméterként, ez típus- és adatkonstruktorok esetében lehetséges.

A kétparaméteres függvények operátorként is definiálhatók, ekkor mind prefix, mind infix formában is használhatók ((*) 3 5 illetve 3 * 5). Operátorok definiálásakor meg kell adni a kötődést (bal- vagy jobbasszociativitás) és a precedenciát (0-9 skálán).

definition module abstract // abstract type ::Complex createComplex :: Real Real -> Complex re :: Complex -> Real im :: Complex -> Real (+^) infixl 6 :: Complex Complex -> Complex ~^ :: Complex -> Complex