Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
As funções são a unidade fundamental de execução de programas em qualquer linguagem de programação. Como em outras linguagens, uma função F# tem um nome, pode ter parâmetros e tomar argumentos, e tem um corpo. F# também suporta construções de programação funcional, 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 selecionadas 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. A função-corpo consiste em uma expressão. A expressão que compõe o corpo da função é tipicamente uma expressão composta que consiste em um número de expressões que culminam em uma expressão final que é o valor de retorno. O return-type é dois pontos seguido de um tipo e é opcional. Se você não especificar o tipo do valor de retorno explicitamente, o compilador determinará o tipo de retorno a partir da expressão final.
Uma definição de função simples é semelhante à seguinte:
let f x = x + 1
No exemplo anterior, o nome da função é f, o argumento é x, que tem type 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 inlineo , consulte Funções embutidas.
Âmbito de aplicação
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 para o parâmetro, o tipo de parâmetro é inferido pelo compilador. Por exemplo, na seguinte definição de função, o argumento x é inferido como sendo 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 a partir 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.
Órgãos de Função
Um corpo de função pode conter definições de variáveis e funções locais. Tais variáveis e funções estão no escopo no corpo da função atual, mas não fora dele. Você deve usar 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 de retorno
O compilador usa a expressão final em um corpo de função para determinar o valor de retorno e o tipo. O compilador pode inferir o tipo da expressão final a partir de expressões anteriores. Na função cylinderVolume, mostrada na seção anterior, o tipo de é determinado a partir do pi 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 está escrito acima, o compilador aplica float a toda a função; Se você pretende 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 cylinderVolume e atribuir o resultado ao valor vol, escreva o seguinte código:
let vol = cylinderVolume 2.0 3.0
Aplicação parcial dos argumentos
Se você fornecer menos do que o número especificado de argumentos, criará uma nova função que espera os argumentos restantes. Este método de manipulação 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 tubo: 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 do tubo 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
Você forneceria o argumento final conforme necessário para vários comprimentos de tubo 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 autodenominam. Eles exigem que você especifique a palavra-chave rec seguindo a palavra-chave let . Invoque a função recursiva de dentro do corpo da função, assim como você invocaria qualquer chamada de função. A função recursiva a seguir calcula o nésimo número de Fibonacci. A sequência numérica de Fibonacci é conhecida desde a antiguidade e é uma sequência em que 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 podem transbordar a pilha de programas ou executar de forma ineficiente se você não escrevê-las com cuidado e com consciência 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 onde os valores são usados. A seguir está um exemplo de uma função que usa um valor de função como um argumento:
let apply1 (transform: int -> int) y = transform y
Você especifica o tipo de um valor de função usando o -> token. No lado esquerdo deste token está o tipo do argumento, e no lado direito está o valor de retorno. No exemplo anterior, apply1 é uma função que toma uma função transform como um argumento, onde transform é uma função que pega 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 de será 101 após a execução do result 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 do Lambda
Uma expressão lambda é uma função sem nome. Nos exemplos anteriores, em vez de definir funções nomeadas increment e mul, 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 palavra-chave fun . 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 a partir do tipo da última expressão no corpo. Para obter mais informações, consulte Expressões do Lambda: a fun palavra-chave.
Tubulações
O operador de pipe |> é usado extensivamente ao processar dados em F#. Este operador permite-lhe estabelecer "pipelines" de funções de uma forma 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 mostra 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 da função
As funções em F# podem ser compostas a partir de outras funções. A composição de duas funções função1 e função2 é outra função que representa a aplicação da função1 seguida pela aplicação 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 assume duas funções e retorna uma função, em contraste, o operador |> de pipeline pega 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 composição, mostrando as diferenças nas assinaturas de função e 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
Funções de sobrecarga
Você pode sobrecarregar métodos de um tipo, mas não funções. Para obter mais informações, consulte Métodos.