2010-09-20 17 views
57

La palabra clave inline en F # me parece tener un propósito algo diferente al que estoy acostumbrado en, por ejemplo, C. Por ejemplo, parece afectar el tipo de una función (¿qué son los "parámetros de tipo resueltos estáticamente"? ¿No se resuelven estáticamente todos los tipos F #?)Uso de `en línea` en F #

¿Cuándo debo usar las funciones inline?

Respuesta

66

La palabra clave inline indica que la definición de una función debe insertarse en línea en cualquier código que la use. La mayoría de las veces, esto no tendrá ningún efecto sobre el tipo de función. Sin embargo, en casos raros, puede conducir a una función que tiene un tipo más general, ya que existen restricciones que no se pueden expresar en la forma compilada del código en .NET, pero que pueden aplicarse cuando la función está en línea.

El primer caso en el que esto se aplica es el uso de operadores.

let add a b = a + b 

tendrá un tipo inferido monomórfica (probablemente int -> int -> int, pero podría ser algo así como float -> float -> float si tiene código que utiliza esta función en ese tipo de lugar). Sin embargo, mediante el marcado de esta función en línea, el F # compilador inferir un tipo polimórfico:

let inline add a b = a + b 
// add has type ^a -> ^b -> ^c when (^a or ^b) : (static member (+) : ^a * ^b -> ^c) 

No hay manera para codificar este tipo de restricción de una manera de primera clase en el código compilado en .NET. Sin embargo, el compilador F # puede hacer cumplir esta restricción en el sitio donde se enlista la función, de modo que todos los usos del operador se resuelvan en tiempo de compilación.

los parámetros tipo ^a, ^b, y ^c son "estáticamente resuelto parámetros de tipo", lo que significa que los tipos de los argumentos deben ser estáticamente conocidos en el sitio donde se están utilizando dichos parámetros. Esto está en contraste con los parámetros de tipo normal (por ejemplo, 'a, 'b, etc.), donde los parámetros significan algo así como "algún tipo que se proporcionará más adelante, pero que puede ser cualquier cosa".

+0

Lo siento pero estoy empezando a aprender F # y no entiendo en absoluto su respuesta, ¿puede decirme alguna URL donde se explican los conceptos que utilizó? – knocte

+1

'' p: 'a'' - requiere que '' p'' sea un descendiente del tipo ''' a'' como '' int'', '' obj'', '' 'a cuando' a: > SomeBaseType'', etc. Por otro lado, '' p:^a'' - requiere que '' p'' sea un tipo que admita algunas características o subtipos en los puntos de invocación en el código haciéndolo directamente o al abrir un módulo de soporte o acceder a una clase de métodos de extensión, etc. – George

8

El F# component design guidelines solo menciona un poco sobre esto. Mi recomendación (que se alinea bien con lo que se dice allí) es:

  • No utilice inline
    • Excepción: es posible considerar el uso de inline al escribir bibliotecas matemáticas para ser consumido por otros # código F y desea para escribir funciones que son genéricas sobre diferentes tipos de datos numéricos.

Hay un montón de otros "interesante" usos de línea y el miembro estático limitaciones de tipo "pato de tipificación" de escenarios que funcionan un poco como plantillas de C++. Mi consejo es evitar todo eso como la peste.

La respuesta de @kvb profundiza sobre qué son las "restricciones de tipo estático".

+6

La palabra clave 'inline' también es extremadamente útil más allá de las bibliotecas matemáticas.Por ejemplo, en el contexto de estructuras de datos donde se puede utilizar para componer estructuras de datos concretas a partir de abstractas sin incurrir en ninguna penalización de rendimiento en tiempo de ejecución. –

+0

¿Cómo se supone que implemente las clases de tipos sin alinear? –

28

¿Cuándo debería utilizar las funciones inline?

La aplicación más valiosa de la palabra clave inline en la práctica es inlining funciones de orden superior al sitio de llamadas donde sus argumentos de la función también se inline con el fin de producir una pieza de forma simple optimizado totalmente de código.

Por ejemplo, el inline en el fold siguiente función hace que sea más rápido × 5:

let inline fold f a (xs: _ []) = 
    let mutable a = a 
    for i=0 to xs.Length-1 do 
     a <- f a xs.[i] 
    a 

Tenga en cuenta que este tiene poco parecido con lo que inline hace en la mayoría de otros idiomas. Puede lograr un efecto similar utilizando la metaprogramación de plantillas en C++, pero F # también puede alinearse entre ensamblados compilados porque inline se transmite a través de metadatos .NET.

+2

¿La biblioteca estándar está plegada? –

+5

@J Cooper: No, pero convierte su argumento de función en un cierre optimizado que ayuda a ciertos tipos. El 'pliegue 'que di es ~ 3 veces más rápido que el' Array.fold' incorporado cuando se aplica a números complejos, por ejemplo. –

+0

Interesante. ¿Habría algún inconveniente en el built-in usando 'inline'? –

15

Debe usar en línea cuando necesite definir una función que debe tener su tipo (re) evaluado en el sitio de cada uso, en oposición a una función normal, que tendrá su tipo evaluado (inferido) solo en el el sitio del primer uso, y luego se considera que está tipificado estáticamente con esa primera firma de tipo inferido en cualquier otro lugar a partir de entonces.

En el caso en línea, la definición de la función es efectivamente genérica/polimórfica, mientras que en el caso normal (ninguno en línea), la función se escribe estáticamente (y a menudo implícitamente).

Por lo tanto, si se utiliza en línea, el siguiente código:

let inline add a b = a + b 

[<EntryPoint>] 
let main args = 

    let one = 1 
    let two = 2 
    let three = add one two 
    // here add has been compiled to take 2 ints and return an int 

    let dog = "dog" 
    let cat = "cat" 
    let dogcat = add dog cat 
    // here add has been compiled to take 2 strings and return a string 

    printfn "%i" three 
    printfn "%s" dogcat 

    0 

edificaré, y compilar para producir el siguiente resultado:

3 
dogcat 

En otras palabras, la definición misma función complemento tiene se ha utilizado para producir una función que se suma a enteros, y una función que concatena dos cadenas (de hecho, la sobrecarga del operador subyacente en + también se logra debajo del capó usando en línea).

Considerando que el presente código, idénticos excepto en línea que la función de complemento ya no se declara:

let add a b = a + b 

[<EntryPoint>] 
let main args = 

    let one = 1 
    let two = 2 
    let three = add one two 
    // here add has been compiled to take 2 ints and return an int 

    let dog = "dog" 
    let cat = "cat" 
    let dogcat = add dog cat 
    // since add was not declared inline, it cannot be recompiled 
    // and so we now have a type mismatch here 

    printfn "%i" three 
    printfn "%s" dogcat 

    0 

no se compilará, en su defecto con esta queja:

let dogcat = add dog cat 
        ^^^ - This expression was expected to have type int 
          but instead has type string 

Un buen ejemplo de cómo el uso en línea es apropiado, es cuando se quiere definir una función genérica para invertir el orden de la aplicación de argumentos de una función con 2 argumentos, por ejemplo

let inline flip f x y = f y x 

como se hace en la respuesta de @pad a esta pregunta Different argument order for getting N-th element of Array, List or Seq.