Functions
A függvények a program végrehajtásának alapvető egységei bármely programozási nyelven. Más nyelvekhez hasonlóan az F#-függvényeknek is van neve, lehetnek paramétereik, argumentumokat vehetnek fel, és törzsük is van. Az F# olyan funkcionális programozási szerkezeteket is támogat, mint például a függvények értékként való kezelése, a kifejezésekben névtelen függvények használata, új függvények kialakítására szolgáló függvények összetétele, curriált függvények és a függvények implicit definíciója a függvényargumentumok részleges alkalmazásával.
A függvények definiálásához használja a let
kulcsszót, vagy ha a függvény rekurzív, a let rec
kulcsszókombinációt.
Syntax
// Non-recursive function definition.
let [inline] function-name parameter-list [ : return-type ] = function-body
// Recursive function definition.
let rec function-name parameter-list = recursive-function-body
Megjegyzések
A függvény neve egy olyan azonosító, amely a függvényt jelöli. A paraméterlista szóközökkel elválasztott egymást követő paraméterekből áll. Minden paraméterhez megadhat egy explicit típust a Paraméterek szakaszban leírtak szerint. Ha nem ad meg egy adott argumentumtípust, a fordító megpróbálja kikövetkeztetni a típust a függvény törzséből. A függvénytörzs egy kifejezésből áll. A függvény törzsét alkotó kifejezés általában egy összetett kifejezés, amely több olyan kifejezésből áll, amelyek a visszatérési értéket tartalmazó végső kifejezésben csúcsosulnak ki. A visszatérési típus egy kettőspont, amelyet egy típus követ, és nem kötelező. Ha nem adja meg explicit módon a visszatérési érték típusát, a fordító határozza meg a visszatérési típust a végső kifejezésből.
Egy egyszerű függvénydefiníció a következőhöz hasonló:
let f x = x + 1
Az előző példában a függvény neve f
, az argumentum, x
amelynek típusa int
, a függvény törzse x + 1
, a visszatérési érték pedig típus int
.
A függvények megjelölhetők inline
. További információ: inline
Beágyazott függvények.
Hatókör
A modul hatókörétől eltérő hatókörszinten nem hiba egy érték vagy függvénynév újbóli felhasználása. Ha újra felhasznál egy nevet, a később deklarált név árnyékot ad a korábban deklarált névnek. A modulok legfelső szintű hatókörében azonban a neveknek egyedinek kell lenniük. A következő kód például hibát eredményez, amikor a modul hatókörében jelenik meg, de nem, ha egy függvényben jelenik meg:
let list1 = [ 1; 2; 3]
// Error: duplicate definition.
let list1 = []
let function1 () =
let list1 = [1; 2; 3]
let list1 = []
list1
A következő kód azonban bármely hatókörszinten elfogadható:
let list1 = [ 1; 2; 3]
let sumPlus x =
// OK: inner list1 hides the outer list1.
let list1 = [1; 5; 10]
x + List.sum list1
Paraméterek
A paraméterek nevei a függvény neve után jelennek meg. A paraméter típusát az alábbi példában látható módon adhatja meg:
let f (x : int) = x + 1
Ha megad egy típust, az a paraméter nevét követi, és kettőspont választja el a névtől. Ha kihagyja a paraméter típusát, a paramétertípust a fordító kikövetkezteti. Az alábbi függvénydefinícióban például az argumentum x
típusra int
van kikövetkeztetve, mert az 1 típus int
.
let f x = x + 1
A fordító azonban megpróbálja a lehető leggenerikusabbá tenni a függvényt. Jegyezze fel például a következő kódot:
let f x = (x, x)
A függvény bármilyen típusú argumentumból rekordot hoz létre. Mivel a típus nincs megadva, a függvény bármilyen argumentumtípussal használható. További információ: Automatikus általánosítás.
Függvénytestek
A függvénytörzsek helyi változók és függvények definícióit tartalmazhatják. Az ilyen változók és függvények hatóköre az aktuális függvény törzsében található, de azon kívül nem. Behúzással kell jeleznie, hogy egy definíció egy függvénytörzsben található, ahogy az alábbi példában látható:
let cylinderVolume radius length =
// Define a local value pi.
let pi = 3.14159
length * pi * radius * radius
További információ: Kódformázási irányelvek és részletes szintaxis.
Értékeket ad vissza
A fordító a függvény törzsében lévő végső kifejezést használja a visszatérési érték és a típus meghatározásához. Előfordulhat, hogy a fordító a korábbi kifejezésekből származó végső kifejezés típusát következteti. Az előző szakaszban látható függvényben cylinderVolume
a függvény típusa pi
a konstans 3.14159
típusából lesz meghatározva float
. A fordító a kifejezés típusának pi
meghatározására használja a típust length * pi * radius * radius
float
. Ezért a függvény általános visszatérési típusa .float
A visszatérési típus explicit megadásához írja be a kódot a következő módon:
let cylinderVolume radius length : float =
// Define a local value pi.
let pi = 3.14159
length * pi * radius * radius
Ahogy a kód fent meg van írva, a fordító a teljes függvényre alkalmazza a lebegőpontos kódot; ha a paramétertípusokra is alkalmazni szeretné, használja a következő kódot:
let cylinderVolume (radius : float) (length : float) : float
Függvény meghívása
A függvényeket úgy hívhatja meg, hogy megadja a függvény nevét, majd egy szóközt, majd az argumentumokat szóközök választják el egymástól. Ha például meghívja a cylinderVolume függvényt, és hozzárendeli az eredményt az vol értékhez, a következő kódot kell írnia:
let vol = cylinderVolume 2.0 3.0
Argumentumok részleges alkalmazása
Ha a megadott számú argumentumnál kevesebbet ad meg, egy új függvényt hoz létre, amely a fennmaradó argumentumokat várja. Az argumentumok kezelésének ezt a módszerét nevezik curryingnek , és olyan funkcionális programozási nyelvek jellemzője, mint az F#. Tegyük fel például, hogy két csőmérettel dolgozik: az egyik sugara 2,0 , a másik sugara 3,0. Az alábbiak szerint hozhat létre olyan függvényeket, amelyek meghatározzák a cső térfogatát:
let smallPipeRadius = 2.0
let bigPipeRadius = 3.0
// These define functions that take the length as a remaining
// argument:
let smallPipeVolume = cylinderVolume smallPipeRadius
let bigPipeVolume = cylinderVolume bigPipeRadius
Ezután a két különböző méretű cső különböző hossza esetén szükség szerint adja meg a végső argumentumot:
let length1 = 30.0
let length2 = 40.0
let smallPipeVol1 = smallPipeVolume length1
let smallPipeVol2 = smallPipeVolume length2
let bigPipeVol1 = bigPipeVolume length1
let bigPipeVol2 = bigPipeVolume length2
Rekurzív függvények
A rekurzív függvények maguknak nevezett függvények. Ezek megkövetelik, hogy a let kulcsszót követően adja meg a rec kulcsszót. Hívja meg a rekurzív függvényt a függvény törzséből ugyanúgy, mint bármely függvényhívást. Az alábbi rekurzív függvény kiszámítja az n-edik Fibonacci-számot. A Fibonacci számsorozat már az ókor óta ismert, és olyan sorozat, amelyben minden egymást követő szám a sorozat előző két számának összege.
let rec fib n = if n < 2 then 1 else fib (n - 1) + fib (n - 2)
Egyes rekurzív függvények túlcsordulhatnak a programveremen, vagy nem hatékonyak, ha nem körültekintően és speciális technikákkal, például a farokrekurzió, az akkumulátorok és a folytatások használatával írják őket.
Függvényértékek
Az F#-ban minden függvény értéknek számít; valójában függvényértékekként ismertek. Mivel a függvények értékek, más függvények argumentumaként vagy más olyan környezetekben is használhatók, ahol értékeket használnak. Az alábbiakban egy példa látható egy függvényre, amely argumentumként vesz fel egy függvényértéket:
let apply1 (transform : int -> int ) y = transform y
A függvényérték típusát a ->
jogkivonat használatával adhatja meg. A jogkivonat bal oldalán található az argumentum típusa, a jobb oldalon pedig a visszatérési érték. Az előző példában egy függvény argumentumként apply1
vesz fel egy függvényt transform
, ahol transform
egy függvény egy egész számot vesz fel, és egy másik egész számot ad vissza. A következő kód bemutatja, hogyan használható apply1
:
let increment x = x + 1
let result1 = apply1 increment 100
Az érték result
101 lesz az előző kód futtatása után.
Több argumentumot egymást követő ->
tokenek választanak el egymástól, ahogy az alábbi példában látható:
let apply2 ( f: int -> int -> int) x y = f x y
let mul x y = x * y
let result2 = apply2 mul 10 20
Az eredmény 200.
Lambda-kifejezések
A lambda kifejezés egy névtelen függvény. Az előző példákban a nevesített függvények növekményének és mul értékének meghatározása helyett a lambda kifejezéseket használhatja az alábbiak szerint:
let result3 = apply1 (fun x -> x + 1) 100
let result4 = apply2 (fun x y -> x * y ) 10 20
A lambda kifejezéseket a fun
kulcsszó használatával definiálhatja. A lambda kifejezés hasonlít egy függvénydefinícióra, azzal a kivételrel, hogy a =
jogkivonat helyett a ->
jogkivonat választja el az argumentumlistát a függvény törzsétől. A normál függvénydefiníciókhoz hasonlóan az argumentumtípusok is kikövetkeztethetők vagy explicit módon megadhatók, a lambda kifejezés visszatérési típusa pedig a törzs utolsó kifejezésének típusából származik. További információ: Lambda-kifejezések: A fun
kulcsszó.
Pipelines
A csőkezelőt |>
széles körben használják az F#-adatok feldolgozásakor. Ez az operátor lehetővé teszi a függvények "folyamatainak" rugalmas létrehozását. A pipelining lehetővé teszi a függvényhívások egymást követő műveletekként való összekapcsolását:
let result = 100 |> function1 |> function2
Az alábbi minta bemutatja, hogyan hozhat létre egy egyszerű funkcionális folyamatot az operátorok használatával:
/// Square the odd values of the input and add one, using F# pipe operators.
let squareAndAddOdd values =
values
|> List.filter (fun x -> x % 2 <> 0)
|> List.map (fun x -> x * x + 1)
let numbers = [ 1; 2; 3; 4; 5 ]
let result = squareAndAddOdd numbers
Az eredmény : [2; 10; 26]
. Az előző minta listafeldolgozó függvényeket használ, amelyek bemutatják, hogyan használhatók a függvények az adatok feldolgozására folyamatok létrehozásakor. Maga a folyamatoperátor az F#-magtárban van definiálva az alábbiak szerint:
let (|>) x f = f x
Függvényösszetétel
Az F# függvényei más függvényekből is összeállíthatók. Két függvényfüggvény1 és függvény2 összetétele egy másik függvény, amely a függvény1 alkalmazását, majd a függvény2 alkalmazását jelöli:
let function1 x = x + 1
let function2 x = x * 2
let h = function1 >> function2
let result5 = h 100
Az eredmény 202.
Az összeállítási operátor >>
két függvényt vesz fel, és egy függvényt ad vissza. Ezzel szemben a folyamatoperátor |>
egy értéket és egy függvényt vesz fel, és egy értéket ad vissza. Az alábbi példakód a folyamat és az összeállítási operátorok közötti különbséget mutatja be a függvényaláírások és a használat különbségeinek szemléltetésével.
// Function composition and pipeline operators compared.
let addOne x = x + 1
let timesTwo x = 2 * x
// Composition operator
// ( >> ) : ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3
let Compose2 = addOne >> timesTwo
// Backward composition operator
// ( << ) : ('T2 -> 'T3) -> ('T1 -> 'T2) -> 'T1 -> 'T3
let Compose1 = addOne << timesTwo
// Result is 5
let result1 = Compose1 2
// Result is 6
let result2 = Compose2 2
// Pipelining
// Pipeline operator
// ( |> ) : 'T1 -> ('T1 -> 'U) -> 'U
let Pipeline2 x = addOne x |> timesTwo
// Backward pipeline operator
// ( <| ) : ('T -> 'U) -> 'T -> 'U
let Pipeline1 x = addOne <| timesTwo x
// Result is 5
let result3 = Pipeline1 2
// Result is 6
let result4 = Pipeline2 2
Túlterhelési függvények
A metódusok túlterhelhetők, de függvények nem. További információ: Metódusok.