Share via


Funções embutidas

Funções embutidas são funções integradas diretamente ao código de chamada.

Como usar funções embutidas

Quando você usa parâmetros de tipo estático, todas as funções parametrizadas por parâmetros de tipo devem estar embutidas. Isso garante que o compilador possa resolver esses parâmetros de tipo. Quando você usa parâmetros de tipo genérico comuns, não há essa restrição.

Além de habilitar o uso de restrições de membro, as funções embutidas podem ser úteis para otimizar o código. No entanto, o uso excessivo de funções embutidas pode fazer com que seu código seja menos resistente a alterações nas otimizações do compilador e à implementação de funções de biblioteca. Por esse motivo, você deve evitar o uso de funções embutidas para otimização, a menos que tenha tentado todas as outras técnicas de otimização. Fazer uma função ou método embutido às vezes pode melhorar o desempenho, mas isso nem sempre acontece. Portanto, você também deve usar medidas de desempenho para verificar se fazer tornar função embutida de fato tem um efeito positivo.

O modificador inline pode ser aplicado a funções no nível superior, no nível do módulo ou no nível do método em uma classe.

O exemplo de código a seguir ilustra uma função embutida no nível superior, um método de instância embutida e um método estático embutido.

let inline increment x = x + 1
type WrapInt32() =
    member inline this.incrementByOne(x) = x + 1
    static member inline Increment(x) = x + 1

Funções embutidas e inferência de tipos

A presença de inline afeta a inferência de tipos. Isso ocorre porque as funções embutidas podem ter parâmetros de tipo estaticamente resolvidos, enquanto as funções não embutidas não podem. O exemplo de código a seguir mostra um caso em que inline é útil porque você está usando uma função que tem um parâmetro de tipo estaticamente resolvido, o operador de conversão float.

let inline printAsFloatingPoint number =
    printfn "%f" (float number)

Sem o modificador inline, a inferência de tipos força a função a usar um tipo específico, nesse caso int. Porém, com o modificador inline, a função também é inferida para ter um parâmetro de tipo estaticamente resolvido. Com o modificador inline, o tipo é inferido como sendo o seguinte:

^a -> unit when ^a : (static member op_Explicit : ^a -> float)

Isso significa que a função aceita qualquer tipo que dê suporte a uma conversão em float.

InlineIfLambda

O compilador F# inclui um otimizador que embute o código. O atributo InlineIfLambda permite que o código indique, opcionalmente, que se um argumento for determinado como uma função lambda, esse argumento deverá ser sempre embutido em sites de chamada. Para mais informações, confira F# RFC FS-1098.

Por exemplo, considere a seguinte função iterateTwice para cruzar uma matriz:

let inline iterateTwice ([<InlineIfLambda>] action) (array: 'T[]) =
    for i = 0 to array.Length-1 do
        action array[i]
    for i = 0 to array.Length-1 do
        action array[i]

Se o site de chamada for:

let arr = [| 1.. 100 |]
let mutable sum = 0
arr  |> iterateTwice (fun x ->
    sum <- sum + x)

Depois de embutir e de outras otimizações, o código se torna:

let arr = [| 1..100 |]
let mutable sum = 0
for i = 0 to arr.Length - 1 do
    sum <- sum + arr[i] 
for i = 0 to arr.Length - 1 do
    sum <- sum + arr[i] 

Essa otimização é aplicada independentemente do tamanho da expressão lambda envolvida. Esse recurso também pode ser usado para implementar o cancelamento de loop e transformações semelhantes de maneira mais confiável.

Um aviso de aceitação (/warnon:3517 ou propriedade <WarnOn>3517</WarnOn>) pode ser ativado para indicar locais em seu código em que argumentos InlineIfLambda não estão associados a expressões lambda em sites de chamada. Em situações normais, esse aviso não deve ser habilitado. No entanto, em determinados tipos de programação de alto desempenho, pode ser útil garantir que todo o código seja embutido e nivelado.

Confira também