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 f
název funkce , argument je , který má x
typ int
, tělo funkce je x + 1
a návratová hodnota je typu int
.
Funkce lze označit inline
. Informace o inline
ná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.