Funções
As funções são a unidade fundamental de execução do programa em qualquer linguagem de programação. Como em outras línguas, uma função F# tem um nome, pode ter parâmetros e ter argumentos, e tem um corpo. F# também suporta construções de programação funcional, tais como tratar funções como valores, usar funções não nomeadas em expressões, composição de funções para formar novas funções, funções curadas e a definição implícita de funções através da aplicação parcial de argumentos de função.
Define funções utilizando a let
palavra-chave ou, se a função for recursiva, a combinação de let rec
palavras-chave.
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
Observações
O nome da função é um identificador que representa a função. A lista de parâmetros consiste em parâmetros sucessivos que são separados por espaços. Pode especificar um tipo explícito para cada parâmetro, conforme descrito na secção Parâmetros. Se não especificar um tipo específico de argumento, o compilador tenta inferir o tipo do corpo da função. A função-corpo consiste numa expressão. A expressão que compõe o corpo da função é tipicamente uma expressão composta que consiste numa série de expressões que culminam numa expressão final que é o valor de retorno. O tipo de retorno é um cólon seguido por um tipo e é opcional. Se não especificar explicitamente o tipo do valor de devolução, o compilador determina o tipo de retorno a partir da expressão final.
Uma definição de função simples assemelha-se ao seguinte:
let f x = x + 1
No exemplo anterior, o nome da função é f
, o argumento é x
, que tem tipo int
, o corpo da função é x + 1
, e o valor de retorno é de tipo int
.
As funções podem ser marcadas inline
. Para obter informações sobre inline
, consulte Funções Inline.
Âmbito
Em qualquer nível de âmbito que não seja o âmbito do módulo, não é um erro reutilizar um valor ou nome de função. Se reutilizar um nome, o nome declarado mais tarde faz sombra ao nome declarado anteriormente. No entanto, no âmbito de nível superior de um módulo, os nomes devem ser únicos. Por exemplo, o seguinte código produz um erro quando aparece no âmbito 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 seguinte código é aceitável em qualquer nível de âmbito:
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. Pode especificar um tipo para um parâmetro, como mostra o seguinte exemplo:
let f (x : int) = x + 1
Se especificar um tipo, segue o nome do parâmetro e é separado do nome por um cólon. Se omitir o tipo para o parâmetro, o tipo de parâmetro é deduzido pelo compilador. Por exemplo, na seguinte definição de função, 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, note o seguinte código:
let f x = (x, x)
A função cria um tuple 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 mais informações, consulte a 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 âmbito do corpo da função atual, mas não fora dela. Deve utilizar o entalhe para indicar que uma definição se encontra num corpo de funções, como mostra o seguinte exemplo:
let cylinderVolume radius length =
// Define a local value pi.
let pi = 3.14159
length * pi * radius * radius
Para obter mais informações, consulte as Diretrizes de Formatação de Código e a Sintaxe Verbose.
Valores de devolução
O compilador utiliza a expressão final num corpo de função para determinar o valor e o tipo de retorno. O compilador pode inferir o tipo de expressão final de expressões anteriores. Na função cylinderVolume
, mostrada na secção anterior, o tipo de pi
é determinado a partir do tipo de literal 3.14159
a ser float
. O compilador utiliza o tipo de pi
para determinar o tipo de expressão length * pi * radius * radius
a ser float
. Portanto, o tipo de retorno geral da função é float
.
Para especificar explicitamente o tipo de devolução, escreva o código da seguinte forma:
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 flutuar para toda a função; Se pretender aplicá-lo também aos tipos de parâmetros, utilize 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 ligar para o cilindro de funçãoVolume e atribuir o resultado ao vol valor, escreva o seguinte código:
let vol = cylinderVolume 2.0 3.0
Aplicação Parcial de Argumentos
Se fornecer menos do que o número especificado de argumentos, criará uma nova função que espera os restantes argumentos. Este método de manipulação de argumentos é referido como caril e é uma característica de linguagens de programação funcionais como F#. Por exemplo, suponha que esteja a trabalhar com dois tamanhos de tubo: um tem um raio de 2.0 e o outro tem um raio de 3.0. Pode criar funções que determinam o volume do tubo da seguinte forma:
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, 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 intitulam. Exigem que especifique a palavra-chave rec seguindo a palavra-chave "deixar ". Invoque a função recursiva dentro do corpo da função, tal como invocaria qualquer chamada de função. A função recursiva seguinte calcula onº número 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 podem transbordar a pilha do programa ou executar ineficientemente se não as escrever com cuidado e com consciência de técnicas especiais, como o uso de recursividade da cauda, acumuladores e continuações.
Valores de função
Em F#, todas as funções são consideradas valores; na verdade, são conhecidos como valores de função. Como as funções são valores, podem ser usados como argumentos para outras funções ou em outros contextos em que os valores são usados. Segue-se um exemplo de uma função que assume um valor de função como argumento:
let apply1 (transform : int -> int ) y = transform y
Especifica o tipo de valor de função utilizando o ->
token. No lado esquerdo deste token está o tipo de argumento, e do lado direito está o valor de retorno. No exemplo anterior, apply1
é uma função que assume uma função transform
como argumento, onde transform
é uma função que toma um inteiro e devolve outro inteiro. O seguinte código mostra como usar apply1
:
let increment x = x + 1
let result1 = apply1 increment 100
O valor será de result
101 após o código anterior.
Vários argumentos são separados por fichas sucessivas ->
, como mostra o seguinte exemplo:
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 funções nomeadas incrementais e mulas, pode utilizar expressões lambda da seguinte forma:
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 assemelha-se 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 numa 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 mais informações, consulte Lambda Expressions: The fun
Keyword.
Pipelines
O operador da |>
tubagem é utilizado extensivamente ao processar dados em F#. Este operador permite-lhe estabelecer "oleodutos" de funções de forma flexível. A pipeta permite acorrentar as chamadas de função como operações sucessivas:
let result = 100 |> function1 |> function2
A amostra que se segue percorre a forma como pode utilizar estes operadores para construir um simples gasoduto funcional:
/// 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]
que. A amostra anterior utiliza funções de processamento de listas, demonstrando como as funções podem ser usadas para processar dados ao construir oleodutos. O próprio operador do gasoduto é definido na biblioteca central F# da seguinte forma:
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çõ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 devolve uma função; por outro lado, o operador |>
do gasoduto tem um valor e uma função e devolve um valor. O exemplo de código que se segue mostra a diferença entre os operadores do gasoduto e da composição, mostrando as diferenças nas assinaturas de função e na utilização.
// 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
Pode sobrecarregar métodos de tipo, mas não funciona. Para mais informações, consulte Métodos.