Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Les fonctions sont l’unité fondamentale de l’exécution du programme dans n’importe quel langage de programmation. Comme dans d’autres langages, une fonction F# a un nom, peut avoir des paramètres et prendre des arguments, et a un corps. F# prend également en charge les constructions de programmation fonctionnelle, telles que le traitement des fonctions comme des valeurs, l’utilisation de fonctions sans nom dans les expressions, la composition de fonctions pour former de nouvelles fonctions, les fonctions curriées et la définition implicite des fonctions par le biais de l’application partielle des arguments de fonction.
Vous définissez des fonctions à l’aide du let
mot clé ou, si la fonction est récursive, la combinaison de let rec
mots clés.
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
Remarques
Le nom de la fonction est un identificateur qui représente la fonction. La liste des paramètres se compose de paramètres successifs séparés par des espaces. Vous pouvez spécifier un type explicite pour chaque paramètre, comme décrit dans la section Paramètres. Si vous ne spécifiez pas de type d’argument spécifique, le compilateur tente de déduire le type du corps de la fonction. Le corps de la fonction se compose d’une expression. L’expression qui compose le corps de la fonction est généralement une expression composée composée d’un certain nombre d’expressions qui aboutissent à une expression finale qui correspond à la valeur de retour. Le type de retour est un signe deux-points suivi d’un type et est facultatif. Si vous ne spécifiez pas explicitement le type de la valeur de retour, le compilateur détermine le type de retour de l’expression finale.
Une définition de fonction simple ressemble à ce qui suit :
let f x = x + 1
Dans l’exemple précédent, le nom de la fonction est f
, l’argument est x
, dont le type est int
, le corps de la fonction est x + 1
, et la valeur de retour est de type int
.
Les fonctions peuvent être marquées inline
. Pour plus d’informations sur inline
, consultez Fonctions inline.
Étendue
À tout niveau d’étendue autre que l’étendue du module, il n’est pas une erreur de réutilisation d’une valeur ou d’un nom de fonction. Si vous réutilisez un nom, le nom déclaré ultérieurement masque le nom déclaré précédemment. Toutefois, dans l’étendue de niveau supérieur d’un module, les noms doivent être uniques. Par exemple, le code suivant génère une erreur lorsqu’il apparaît dans l’étendue du module, mais pas lorsqu’il apparaît à l’intérieur d’une fonction :
let list1 = [ 1; 2; 3 ]
// Error: duplicate definition.
let list1 = []
let function1 () =
let list1 = [ 1; 2; 3 ]
let list1 = []
list1
Toutefois, le code suivant est acceptable à n’importe quel niveau de portée :
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ètres
Les noms des paramètres sont répertoriés après le nom de la fonction. Vous pouvez spécifier un type pour un paramètre, comme illustré dans l’exemple suivant :
let f (x: int) = x + 1
Si vous spécifiez un type, il suit le nom du paramètre et est séparé du nom par un signe deux-points. Si vous omettez le type du paramètre, le type de paramètre est déduit par le compilateur. Par exemple, dans la définition de fonction suivante, l’argument x
est déduit comme étant de type int
, car 1 est de type int
.
let f x = x + 1
Toutefois, le compilateur tente de rendre la fonction aussi générique que possible. Par exemple, notez le code suivant :
let f x = (x, x)
La fonction crée un tuple à partir d’un argument de n’importe quel type. Étant donné que le type n’est pas spécifié, la fonction peut être utilisée avec n’importe quel type d’argument. Pour plus d’informations, consultez Généralisation automatique.
Corps de fonction
Un corps de fonction peut contenir des définitions de variables et de fonctions locales. Ces variables et fonctions sont dans l’étendue dans le corps de la fonction actuelle, mais pas en dehors. Vous devez utiliser la mise en retrait pour indiquer qu’une définition se trouve dans un corps de fonction, comme illustré dans l’exemple suivant :
let cylinderVolume radius length =
// Define a local value pi.
let pi = 3.14159
length * pi * radius * radius
Pour plus d’informations, consultez les instructions de mise en forme du code et la syntaxe détaillée.
Valeurs de retour
Le compilateur utilise l’expression finale dans un corps de fonction pour déterminer la valeur et le type de retour. Le compilateur peut déduire le type de l’expression finale des expressions précédentes. Dans la fonction cylinderVolume
, indiquée dans la section précédente, le type de pi
celui-ci est déterminé à partir du type du littéral 3.14159
à être float
. Le compilateur utilise le type de pi
l’expression pour déterminer le type de l’expression length * pi * radius * radius
.float
Par conséquent, le type de retour global de la fonction est float
.
Pour spécifier explicitement le type de retour, écrivez le code comme suit :
let cylinderVolume radius length : float =
// Define a local value pi.
let pi = 3.14159
length * pi * radius * radius
Comme le code est écrit ci-dessus, le compilateur applique float à l’ensemble de la fonction ; si vous voulez également l’appliquer aux types de paramètres, utilisez le code suivant :
let cylinderVolume (radius: float) (length: float) : float
Appel d’une fonction
Vous appelez des fonctions en spécifiant le nom de la fonction suivi d’un espace, puis tous les arguments séparés par les espaces. Par exemple, pour appeler le cylindre de fonctionVolume et affecter le résultat au vol de valeur, vous écrivez le code suivant :
let vol = cylinderVolume 2.0 3.0
Application partielle des arguments
Si vous fournissez moins que le nombre spécifié d’arguments, vous créez une fonction qui attend les arguments restants. Cette méthode de gestion des arguments est appelée currying et est une caractéristique de langages de programmation fonctionnels comme F#. Par exemple, supposons que vous travaillez avec deux tailles de canal : un rayon de 2,0 et l’autre a un rayon de 3,0. Vous pouvez créer des fonctions qui déterminent le volume de canal comme suit :
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
Vous devez ensuite fournir l’argument final selon les besoins pour différentes longueurs de canal des deux tailles différentes :
let length1 = 30.0
let length2 = 40.0
let smallPipeVol1 = smallPipeVolume length1
let smallPipeVol2 = smallPipeVolume length2
let bigPipeVol1 = bigPipeVolume length1
let bigPipeVol2 = bigPipeVolume length2
Fonctions récursives
Les fonctions récursives sont des fonctions qui s’appellent elles-mêmes. Ils nécessitent que vous spécifiiez le mot clé rec suivant le mot clé let . Appelez la fonction récursive à partir du corps de la fonction comme vous le feriez pour appeler n’importe quel appel de fonction. La fonction récursive suivante calcule le nième nombre Fibonacci. La séquence de nombres Fibonacci a été connue depuis l’antiquité et est une séquence dans laquelle chaque nombre successif est la somme des deux nombres précédents dans la séquence.
let rec fib n =
if n < 2 then 1 else fib (n - 1) + fib (n - 2)
Certaines fonctions récursives peuvent dépasser la pile du programme ou s’effectuer de manière inefficace si vous ne les écrivez pas avec soin et avec conscience des techniques spéciales, telles que l’utilisation de la récursivité de la queue, des accumulations et des continuations.
Valeurs de fonction
En F#, toutes les fonctions sont considérées comme des valeurs ; en fait, elles sont appelées valeurs de fonction. Étant donné que les fonctions sont des valeurs, elles peuvent être utilisées comme arguments pour d’autres fonctions ou dans d’autres contextes où les valeurs sont utilisées. Voici un exemple de fonction qui prend une valeur de fonction comme argument :
let apply1 (transform: int -> int) y = transform y
Vous spécifiez le type d’une valeur de fonction à l’aide du ->
jeton. Sur le côté gauche de ce jeton est le type de l’argument, et sur le côté droit est la valeur de retour. Dans l’exemple précédent, apply1
est une fonction qui prend une fonction transform
en tant qu’argument, où transform
est une fonction qui prend un entier et retourne un autre entier. Le code suivant montre comment utiliser apply1
:
let increment x = x + 1
let result1 = apply1 increment 100
La valeur de result
101 est 101 après l’exécution du code précédent.
Plusieurs arguments sont séparés par des jetons successifs ->
, comme illustré dans l’exemple suivant :
let apply2 (f: int -> int -> int) x y = f x y
let mul x y = x * y
let result2 = apply2 mul 10 20
Le résultat est 200.
Lambda Expressions
Une expression lambda est une fonction sans nom. Dans les exemples précédents, au lieu de définir l’incrément et le mul des fonctions nommées, vous pouvez utiliser des expressions lambda comme suit :
let result3 = apply1 (fun x -> x + 1) 100
let result4 = apply2 (fun x y -> x * y) 10 20
Vous définissez des expressions lambda à l’aide du fun
mot clé. Une expression lambda ressemble à une définition de fonction, sauf que, au lieu du jeton, le ->
jeton est utilisé pour séparer la liste d’arguments du corps de =
la fonction. Comme dans une définition de fonction régulière, les types d’arguments peuvent être déduits ou spécifiés explicitement, et le type de retour de l’expression lambda est déduit du type de la dernière expression dans le corps. Pour plus d’informations, consultez Expressions lambda : Le fun
mot clé.
Conduites
L’opérateur de canal |>
est utilisé en grande partie lors du traitement des données en F#. Cet opérateur vous permet d’établir des « pipelines » de fonctions de manière flexible. Le pipelining permet aux appels de fonction d’être chaînés en tant qu’opérations successives :
let result = 100 |> function1 |> function2
L’exemple suivant explique comment utiliser ces opérateurs pour créer un pipeline fonctionnel simple :
/// 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
Le résultat est [2; 10; 26]
. L’exemple précédent utilise des fonctions de traitement de liste, montrant comment les fonctions peuvent être utilisées pour traiter les données lors de la création de pipelines. L’opérateur de pipeline lui-même est défini dans la bibliothèque principale F# comme suit :
let (|>) x f = f x
Composition de la fonction
Les fonctions en F# peuvent être composées à partir d’autres fonctions. La composition de deux fonctions function1 et function2 est une autre fonction qui représente l’application de function1 suivie de l’application de function2 :
let function1 x = x + 1
let function2 x = x * 2
let h = function1 >> function2
let result5 = h 100
Le résultat est 202.
L’opérateur >>
de composition prend deux fonctions et retourne une fonction ; en revanche, l’opérateur |>
de pipeline prend une valeur et une fonction et retourne une valeur. L’exemple de code suivant montre la différence entre les opérateurs de pipeline et de composition en montrant les différences dans les signatures de fonction et l’utilisation.
// 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
Fonctions surchargées
Vous pouvez surcharger des méthodes d’un type, mais pas des fonctions. Pour plus d’informations, consultez Méthodes.