Sdílet prostřednictvím


Funkce

Funkce jsou základní jednotkou provádění programů v libovolném programovacím jazyce. Stejně jako v jiných jazycích má funkce 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í nenáznamných funkcí ve výrazech, složení funkcí pro vytváření 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.

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

Poznámky

Název funkce je identifikátor, který představuje funkci. Seznam parametrů se skládá z následný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é vyvrcholí konečným výrazem, který je vrácenou hodnotou. Návratový typ je dvojtečka následovaná typem a je volitelná. Pokud explicitně nezadáte typ návratové hodnoty, kompilátor určí návratový typ z konečného výrazu.

Jednoduchá definice funkce se podobá následující:

let f x = x + 1

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

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

Obor

Na jakékoli úrovni oboru jiného než oboru modulu se nejedná o chybu opakované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ě modulu ale názvy musí být jedinečné. Například následující kód vytvoří 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 libovolné ú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, řídí se názvem parametru a je oddělen od názvu dvojtekou. Pokud vynecháte typ parametru, typ parametru je odvozen kompilátorem. 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 ale pokusí funkci co nejogenerovat. Poznamenejte si například následující kód:

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 najdete 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. Pomocí odsazení je nutné určit, že definice je v těle funkce, jak je znázorněno v následujícím příkladu:

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

Další informace najdete v tématu Pokyny pro formátování kódu a podrobnou syntaxi.

Návratové hodnoty

Kompilátor používá konečný výraz v textu 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ů. V funkci cylinderVolume, zobrazený v předchozí části, typ pi 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 explicitně zadat návratový typ, 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 charakteristická pro funkční programovací jazyky, 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 byste podle potřeby dodali konečný argument 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é se nazývají samy. Vyžadují, abyste zadali klíčové slovo rec za klíčovým slovem let . Vyvolá rekurzivní funkci z těla funkce stejně jako vyvolání jakéhokoli volání funkce. Následující rekurzivní funkce vypočítá nfibonacci číslo. Fibonacciho číselná posloupnost byla známa od starosti a je posloupnost, ve které každé po sobě jdoucí číslo je součet předchozích dvou čísel v posloupnosti.

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

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

Hodnoty funkcí

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 jsou funkce hodnotami, 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 vrácená 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.

Lambda – výrazy

Výraz lambda je nepojmenovaná funkce. V předchozích příkladech můžete místo definování pojmenovaných funkcí zvýšit a mulovat výrazy lambda následujícím způsobem:

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 textu funkce. Stejně jako v definici regulární funkce lze typy argumentů explicitně odvodit nebo zadat a návratový typ výrazu lambda je odvozen z typu posledního výrazu v těle. Další informace najdete v tématu Výrazy lambda: fun Klíčové slovo.

Pipelines

Operátor |> kanálu 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ězování volání funkcí jako následných operací:

let result = 100 |> function1 |> function2

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


/// 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 pro zpracování seznamů, které demonstrují, jak se dají 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 aplikaci funkce1 následovanou 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 vrátí hodnotu. Následující příklad kódu ukazuje rozdíl mezi operátory kanálu a složení zobrazením rozdílů v podpisech a využ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í

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

Viz také