Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
As funções são a unidade fundamental da execução do programa em qualquer linguagem de programação. Como em outras linguagens, uma função F# tem um nome, pode ter parâmetros e usar argumentos e tem um corpo. O F# também dá suporte a construções de programação funcionais, como tratar funções como valores, usar funções sem nome em expressões, composição de funções para formar novas funções, funções curried e a definição implícita de funções por meio da aplicação parcial de argumentos de função.
Você define funções usando a let palavra-chave ou, se a função for recursiva, a combinação de let rec palavras-chave.
Sintaxe
// 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
Observações
O nome da função é um identificador que representa a função. A lista de parâmetros consiste em parâmetros sucessivos separados por espaços. Você pode especificar um tipo explícito para cada parâmetro, conforme descrito na seção Parâmetros. Se você não especificar um tipo de argumento específico, o compilador tentará inferir o tipo do corpo da função. O corpo da função consiste em uma expressão. A expressão que compõe o corpo da função normalmente é uma expressão composta que consiste em várias expressões que culminam em uma expressão final que é o valor retornado. O tipo de retorno é um dois-pontos seguido por um tipo e é opcional. Se você não especificar explicitamente o tipo do valor retornado, o compilador determinará o tipo de retorno da expressão final.
Uma definição de função simples se assemelha ao seguinte:
let f x = x + 1
No exemplo anterior, o nome da função é f, o argumento é x, que tem o tipo int, o corpo da função é x + 1 e o valor de retorno é do tipo int.
As funções podem ser marcadas inline. Para obter informações sobre inline, consulte Funções Embutidas.
Scope
Em qualquer nível de escopo diferente do escopo do módulo, não é um erro reutilizar um valor ou nome de função. Se você reutilizar um nome, o nome declarado posteriormente sombreia o nome declarado anteriormente. No entanto, no escopo de nível superior em um módulo, os nomes devem ser exclusivos. Por exemplo, o código a seguir produz um erro quando aparece no escopo do módulo, mas não quando aparece dentro de uma função:
let list1 = [ 1; 2; 3 ]
// Error: duplicate definition.
let list1 = []
let function1 () =
let list1 = [ 1; 2; 3 ]
let list1 = []
list1
Mas o código a seguir é aceitável em qualquer nível de escopo:
let list1 = [ 1; 2; 3 ]
let sumPlus x =
// OK: inner list1 hides the outer list1.
let list1 = [ 1; 5; 10 ]
x + List.sum list1
Parâmetros
Os nomes dos parâmetros são listados após o nome da função. Você pode especificar um tipo para um parâmetro, conforme mostrado no exemplo a seguir:
let f (x: int) = x + 1
Se você especificar um tipo, ele seguirá o nome do parâmetro e será separado do nome por dois-pontos. Se você omitir o tipo do parâmetro, o tipo de parâmetro será inferido pelo compilador. Por exemplo, na definição de função a seguir, o argumento x é inferido para ser do tipo int porque 1 é do tipo int.
let f x = x + 1
No entanto, o compilador tentará tornar a função o mais genérica possível. Por exemplo, observe o seguinte código:
let f x = (x, x)
A função cria uma tupla de um argumento de qualquer tipo. Como o tipo não é especificado, a função pode ser usada com qualquer tipo de argumento. Para obter mais informações, consulte Generalização Automática.
Corpos de funções
Um corpo de função pode conter definições de variáveis e funções locais. Essas variáveis e funções estão no escopo no corpo da função atual, mas não fora dela. Você deve usar o recuo para indicar que uma definição está em um corpo de função, conforme mostrado no exemplo a seguir:
let cylinderVolume radius length =
// Define a local value pi.
let pi = 3.14159
length * pi * radius * radius
Para obter mais informações, consulte Diretrizes de Formatação de Código e Sintaxe Detalhada.
Valores retornados
O compilador usa a expressão final em um corpo de função para determinar o valor e o tipo retornados. O compilador pode inferir o tipo da expressão final de expressões anteriores. Na função cylinderVolume, mostrada na seção anterior, o tipo de pi é determinado do tipo do literal 3.14159 a ser float. O compilador usa o tipo de pi para determinar o tipo da expressão length * pi * radius * radius a ser float. Portanto, o tipo de retorno geral da função é float.
Para especificar o tipo de retorno explicitamente, escreva o código da seguinte maneira:
let cylinderVolume radius length : float =
// Define a local value pi.
let pi = 3.14159
length * pi * radius * radius
Como o código é escrito acima, o compilador aplica float à função inteira; se você quiser aplicá-lo aos tipos de parâmetro também, use o seguinte código:
let cylinderVolume (radius: float) (length: float) : float
Chamando uma função
Você chama funções especificando o nome da função seguido por um espaço e, em seguida, quaisquer argumentos separados por espaços. Por exemplo, para chamar a função de cilindroVolume e atribuir o resultado ao valor vol, você escreve o seguinte código:
let vol = cylinderVolume 2.0 3.0
Aplicação parcial de argumentos
Se você fornecer menos do que o número especificado de argumentos, criará uma nova função que espera os argumentos restantes. Esse método de tratamento de argumentos é conhecido como currying e é uma característica de linguagens de programação funcionais como F#. Por exemplo, suponha que você esteja trabalhando com dois tamanhos de pipe: um tem um raio de 2,0 e o outro tem um raio de 3,0. Você pode criar funções que determinam o volume de pipe da seguinte maneira:
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
Em seguida, você fornecerá o argumento final conforme necessário para vários comprimentos de pipe dos dois tamanhos diferentes:
let length1 = 30.0
let length2 = 40.0
let smallPipeVol1 = smallPipeVolume length1
let smallPipeVol2 = smallPipeVolume length2
let bigPipeVol1 = bigPipeVolume length1
let bigPipeVol2 = bigPipeVolume length2
Funções recursivas
Funções recursivas são funções que se chamam. Eles exigem que você especifique a palavra-chave rec após a palavra-chave let . Invoque a função recursiva de dentro do corpo da função da mesma forma que você invocaria qualquer chamada de função. A função recursiva a seguir calcula o nnúmero de Fibonacci. A sequência de números de Fibonacci é conhecida desde a antiguidade e é uma sequência na qual cada número sucessivo é a soma dos dois números anteriores na sequência.
let rec fib n =
if n < 2 then 1 else fib (n - 1) + fib (n - 2)
Algumas funções recursivas poderão estourar a pilha do programa ou ser executadas de forma ineficiente se você não as escrever com cuidado e com reconhecimento de técnicas especiais, como o uso de recursão de cauda, acumuladores e continuações.
Valores de função
Em F#, todas as funções são consideradas valores; na verdade, eles são conhecidos como valores de função. Como as funções são valores, elas podem ser usadas como argumentos para outras funções ou em outros contextos em que os valores são usados. Veja a seguir um exemplo de uma função que usa um valor de função como argumento:
let apply1 (transform: int -> int) y = transform y
Especifique o tipo de um valor de função usando o -> token. No lado esquerdo desse token está o tipo do argumento e, no lado direito, está o valor retornado. No exemplo anterior, apply1 é uma função que usa uma função transform como argumento, onde transform é uma função que usa um inteiro e retorna outro inteiro. O código a seguir mostra como usar apply1:
let increment x = x + 1
let result1 = apply1 increment 100
O valor será result 101 após a execução do código anterior.
Vários argumentos são separados por tokens sucessivos -> , conforme mostrado no exemplo a seguir:
let apply2 (f: int -> int -> int) x y = f x y
let mul x y = x * y
let result2 = apply2 mul 10 20
O resultado é 200.
Expressões Lambda
Uma expressão lambda é uma função sem nome. Nos exemplos anteriores, em vez de definir incremento e mul de funções nomeadas, você poderia usar expressões lambda da seguinte maneira:
let result3 = apply1 (fun x -> x + 1) 100
let result4 = apply2 (fun x y -> x * y) 10 20
Você define expressões lambda usando a fun palavra-chave. Uma expressão lambda se assemelha a uma definição de função, exceto que, em vez do = token, o -> token é usado para separar a lista de argumentos do corpo da função. Como em uma definição de função regular, os tipos de argumento podem ser inferidos ou especificados explicitamente e o tipo de retorno da expressão lambda é inferido do tipo da última expressão no corpo. Para obter mais informações, consulte Expressões lambda: a fun palavra-chave.
Pipelines
O operador de pipe |> é usado extensivamente ao processar dados em F#. Esse operador permite que você estabeleça "pipelines" de funções de maneira flexível. O pipelining permite que as chamadas de função sejam encadeadas como operações sucessivas:
let result = 100 |> function1 |> function2
O exemplo a seguir explica como você pode usar esses operadores para criar um pipeline funcional simples:
/// 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
O resultado é [2; 10; 26]. O exemplo anterior usa funções de processamento de lista, demonstrando como as funções podem ser usadas para processar dados ao criar pipelines. O próprio operador de pipeline é definido na biblioteca principal do F# da seguinte maneira:
let (|>) x f = f x
Composição de função
As funções em F# podem ser compostas de outras funções. A composição de duas funções function1 e function2 é outra função que representa a aplicação da função1 seguida pelo aplicativo da função2:
let function1 x = x + 1
let function2 x = x * 2
let h = function1 >> function2
let result5 = h 100
O resultado é 202.
O operador >> de composição usa duas funções e retorna uma função; por outro lado, o operador |> de pipeline usa um valor e uma função e retorna um valor. O exemplo de código a seguir mostra a diferença entre os operadores de pipeline e de composição mostrando as diferenças nas assinaturas de função e no uso.
// 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
Sobrecarregando funções
Você pode sobrecarregar métodos de um tipo, mas não funções. Para obter mais informações, consulte Métodos.