Sdílet prostřednictvím


Functions

Funkce jsou základní jednotkou provádění programů v libovolném programovacím jazyce. Stejně jako v jiných jazycích má funkce jazyka F# název, může mít parametry a přijímat argumenty a má tělo. Jazyk F# také podporuje funkční programovací konstrukce, jako je zacházení s funkcemi jako s hodnotami, používání nepojmenovaných funkcí ve výrazech, složení funkcí pro formování nových funkcí, složených funkcí a implicitní definice funkcí prostřednictvím částečného použití argumentů funkce.

Funkce definujete pomocí klíčového let slova, nebo pokud je funkce rekurzivní, let rec kombinace klíčových slov.

Syntaxe

// 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

Poznámky

Název funkce je identifikátor, který představuje funkci. Seznam parametrů se skládá z po sobě jdoucích parametrů oddělených mezerami. Pro každý parametr můžete zadat explicitní typ, jak je popsáno v části Parametry. Pokud nezadáte konkrétní typ argumentu, kompilátor se pokusí odvodit typ z těla funkce. Tělo funkce se skládá z výrazu. Výraz, který tvoří tělo funkce, je obvykle složený výraz sestávající z řady výrazů, které v konečném výrazu vyvrchlí, což je návratová hodnota. Návratový typ je dvojtečka následovaná typem a je nepovinná. Pokud nezadáte typ návratové hodnoty explicitně, kompilátor určí návratový typ z konečného výrazu.

Jednoduchá definice funkce vypadá takto:

let f x = x + 1

V předchozím příkladu je název funkce , argument je fx, který má typ int, tělo funkce je x + 1a návratová hodnota je typu int.

Funkce lze označit inline. Informace o inlinefunkci Inline Functions naleznete v tématu Vložené funkce.

Scope

Na jakékoli úrovni oboru kromě oboru modulu se nejedná o chybu opětovného použití hodnoty nebo názvu funkce. Pokud znovu použijete název, název deklarovaný později stínuje název deklarovaný dříve. V oboru nejvyšší úrovně v modulu však musí být názvy jedinečné. Například následující kód vygeneruje chybu, když se zobrazí v oboru modulu, ale ne, když se zobrazí uvnitř funkce:

let list1 = [ 1; 2; 3 ]
// Error: duplicate definition.
let list1 = []

let function1 () =
    let list1 = [ 1; 2; 3 ]
    let list1 = []
    list1

Následující kód je ale přijatelný na jakékoli úrovni rozsahu:

let list1 = [ 1; 2; 3 ]

let sumPlus x =
    // OK: inner list1 hides the outer list1.
    let list1 = [ 1; 5; 10 ]
    x + List.sum list1

Parametry

Názvy parametrů jsou uvedeny za názvem funkce. Můžete zadat typ parametru, jak je znázorněno v následujícím příkladu:

let f (x: int) = x + 1

Pokud zadáte typ, následuje název parametru a je oddělen od názvu dvojtečka. Pokud typ parametru vynecháte, kompilátor odvozuje typ parametru. Například v následující definici funkce je argument x odvozen jako typ int , protože 1 je typu int.

let f x = x + 1

Kompilátor se však pokusí provést funkci co nejogeneranější. Všimněte si například následujícího kódu:

let f x = (x, x)

Funkce vytvoří řazenou kolekci členů z jednoho argumentu libovolného typu. Vzhledem k tomu, že typ není zadán, lze funkci použít s libovolným typem argumentu. Další informace naleznete v tématu Automatická generalizace.

Těla funkcí

Tělo funkce může obsahovat definice místních proměnných a funkcí. Tyto proměnné a funkce jsou v oboru v těle aktuální funkce, ale ne mimo ni. K označení, že definice je v těle funkce, jak je znázorněno v následujícím příkladu, musíte použít odsazení:

let cylinderVolume radius length =
    // Define a local value pi.
    let pi = 3.14159
    length * pi * radius * radius

Další informace naleznete v tématu Pokyny pro formátování kódu a podrobná syntaxe.

Návratové hodnoty

Kompilátor používá konečný výraz v těle funkce k určení návratové hodnoty a typu. Kompilátor může odvodit typ konečného výrazu z předchozích výrazů. Ve funkci cylinderVolume, která je uvedena v předchozí části, pi typ je určen z typu literálu 3.14159 , který má být float. Kompilátor používá typ pi k určení typu výrazu length * pi * radius * radius , který má být float. Proto je celkový návratový typ funkce float.

Pokud chcete zadat návratový typ explicitně, napište kód následujícím způsobem:

let cylinderVolume radius length : float =
    // Define a local value pi.
    let pi = 3.14159
    length * pi * radius * radius

Jak je kód napsaný výše, kompilátor použije float na celou funkci; Pokud chcete použít i u typů parametrů, použijte následující kód:

let cylinderVolume (radius: float) (length: float) : float

Volání funkce

Funkce voláte tak, že zadáte název funkce následovaný mezerou a potom všechny argumenty oddělené mezerami. Pokud například chcete volat funkci cylinderVolume a přiřadit výsledek k hodnotě vol, napíšete následující kód:

let vol = cylinderVolume 2.0 3.0

Částečné použití argumentů

Pokud zadáte méně než zadaný počet argumentů, vytvoříte novou funkci, která očekává zbývající argumenty. Tato metoda zpracování argumentů se označuje jako currying a je charakteristickou vlastností funkčních programovacích jazyků, jako je F#. Předpokládejme například, že pracujete se dvěma velikostmi potrubí: jeden má poloměr 2,0 a druhý má poloměr 3,0. Můžete vytvořit funkce, které určují objem kanálu následujícím způsobem:

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

Pak zadáte poslední argument podle potřeby pro různé délky potrubí dvou různých velikostí:

let length1 = 30.0
let length2 = 40.0
let smallPipeVol1 = smallPipeVolume length1
let smallPipeVol2 = smallPipeVolume length2
let bigPipeVol1 = bigPipeVolume length1
let bigPipeVol2 = bigPipeVolume length2

Rekurzivní funkce

Rekurzivní funkce jsou funkce , které volají samy sebe. Vyžadují, abyste zadali klíčové slovo rec za klíčovým slovem let . Vyvolá rekurzivní funkci z těla funkce stejně, jako byste vyvolali jakékoli volání funkce. Následující rekurzivní funkce vypočítá n fibonacciho číslo. Fibonacciho číselná sekvence byla známa od antikvity a je posloupnost, ve které každé po sobě jdoucí číslo je součet předchozích dvou čísel v sekvenci.

let rec fib n =
    if n < 2 then 1 else fib (n - 1) + fib (n - 2)

Některé rekurzivní funkce můžou přetékat zásobník programu nebo neefektivně provádět, pokud je nezapisujete opatrně a s vědomím speciálních technik, jako je použití rekurze ocasu, akumulátorů a pokračování.

Hodnoty funkce

V jazyce F# jsou všechny funkce považovány za hodnoty; ve skutečnosti se označují jako hodnoty funkcí. Vzhledem k tomu, že funkce jsou hodnoty, lze je použít jako argumenty pro jiné funkce nebo v jiných kontextech, kde se hodnoty používají. Následuje příklad funkce, která jako argument přebírá hodnotu funkce:

let apply1 (transform: int -> int) y = transform y

Typ hodnoty funkce zadáte pomocí tokenu -> . Na levé straně tohoto tokenu je typ argumentu a na pravé straně je návratová hodnota. V předchozím příkladu je funkce, apply1 která přebírá funkci transform jako argument, kde transform je funkce, která přebírá celé číslo a vrací další celé číslo. Následující kód ukazuje, jak používat apply1:

let increment x = x + 1

let result1 = apply1 increment 100

Hodnota result bude 101 po spuštění předchozího kódu.

Několik argumentů je odděleno následnými -> tokeny, jak je znázorněno v následujícím příkladu:

let apply2 (f: int -> int -> int) x y = f x y

let mul x y = x * y

let result2 = apply2 mul 10 20

Výsledek je 200.

Výrazy lambda

Výraz lambda je nepojmenovaná funkce. V předchozích příkladech můžete místo definování pojmenovaných funkcí inkrementace a mul použít výrazy lambda takto:

let result3 = apply1 (fun x -> x + 1) 100

let result4 = apply2 (fun x y -> x * y) 10 20

Výrazy lambda definujete pomocí klíčového fun slova. Výraz lambda se podobá definici funkce s tím rozdílem, -> že místo = tokenu se token používá k oddělení seznamu argumentů od těla funkce. Stejně jako v definici regulární funkce lze typy argumentů odvodit nebo zadat explicitně a návratový typ výrazu lambda je odvozen z typu posledního výrazu v těle. Další informace naleznete v tématu Výrazy lambda: Klíčové fun slovo.

Pipelines

Operátor potrubí |> se při zpracování dat v jazyce F# používá značně. Tento operátor umožňuje flexibilním způsobem vytvořit kanály funkcí. Pipelining umožňuje zřetězeným voláním funkcí jako následných operací:

let result = 100 |> function1 |> function2

Následující ukázka vás provede postupem vytvoření jednoduchého funkčního kanálu pomocí těchto operátorů:

/// 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

Výsledek je [2; 10; 26]. Předchozí ukázka používá funkce zpracování seznamu, které demonstrují, jak lze funkce použít ke zpracování dat při vytváření kanálů. Samotný operátor kanálu je definován v základní knihovně jazyka F#následujícím způsobem:

let (|>) x f = f x

Složení funkce

Funkce v jazyce F# se dají skládat z jiných funkcí. Složení dvou funkcí function1 a function2 je další funkce, která představuje použití funkce1 následované aplikací funkce2:

let function1 x = x + 1
let function2 x = x * 2
let h = function1 >> function2
let result5 = h 100

Výsledek je 202.

Operátor >> složení přebírá dvě funkce a vrací funkci. Naproti tomu operátor |> kanálu přebírá hodnotu a funkci a vrací hodnotu. Následující příklad kódu ukazuje rozdíl mezi kanálem a operátory složení zobrazením rozdílů v podpisech a použití funkce.

// 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

Přetížení funkcí

Můžete přetížit metody typu, ale ne funkce. Další informace naleznete v tématu Metody.

Viz také