inline
在我看来,F#中的关键字的用途与我在C中所使用的关键字有所不同。例如,它似乎影响一个函数的类型(“静态解析的类型参数是什么”?不是所有F#类型吗?静态解决?)
什么时候应该使用inline
函数?
Answers:
的inline
关键字表示一个函数定义应该内联插入其使用的任何代码。在大多数情况下,这不会对函数的类型产生任何影响。但是,在极少数情况下,它会导致函数具有更通用的类型,因为存在一些约束不能以.NET中的代码的编译形式表示,但是可以在内联函数时强制执行。
适用的主要情况是使用运算符。
let add a b = a + b
将具有单态推断的类型(可能是int -> int -> int
,但是float -> float -> float
如果您有在该类型处使用该函数的代码,则可能类似)。但是,通过内联标记此函数,F#编译器将推断出多态类型:
let inline add a b = a + b
// add has type ^a -> ^b -> ^c when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> ^c)
无法在.NET上的已编译代码中以一流的方式对此类型约束进行编码。但是,F#编译器可以在内联函数的位置强制执行此约束,以便在编译时解决所有运算符的使用。
类型参数^a
,^b
和^c
是“静态解析的类型参数”,这意味着参数的类型必须在使用这些参数的站点处为静态已知。这与普通类型的参数(例如'a
,'b
等)相反,在常规类型的参数中,这些参数表示“稍后将提供的某种类型,但可以是任何类型”。
p:'a
-需要p
是类型的后代'a
,例如int
,obj
,'a when 'a :> SomeBaseType
等。另一方面,p:^a
-要求p
是支持直接这样做或由一些特征或亚型在代码的调用点具有敞开的支撑模块类型或访问扩展方法类等
当您需要定义必须在每种用法的位置(重新)评估其类型的函数时,应该使用内联,而不是仅在初次使用的位置对其类型进行评估(推断)的普通函数。 ,然后将其视为使用此第一个推断出的类型签名进行静态类型化,此后在其他任何地方进行。
在内联的情况下,函数定义实际上是通用/多态的,而在普通(无内联)情况下,函数是静态(并且通常是隐式)类型的。
因此,如果使用内联,则以下代码:
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
将编译,构建并运行以产生以下输出:
3
dogcat
换句话说,相同的add函数定义已用于生成加到整数的函数以及连接两个字符串的函数(实际上,+的底层运算符重载也可以在后台使用内联实现)。
这段代码是相同的,除了不再以内联方式声明add函数:
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
不会编译,失败的原因是:
let dogcat = add dog cat
^^^ - This expression was expected to have type int
but instead has type string
当您要定义高阶函数(HOF,即以(其他)函数作为参数的函数)时,可以使用内联的一个很好的例子,例如,一个通用函数可以颠倒a具有2个参数的函数,例如
let inline flip f x y = f y x
就像@pad对此问题的回答一样,获取Array,List或Seq的第N个元素的参数顺序不同。
什么时候应该使用
inline
函数?
inline
在实践中,关键字最有价值的应用是将高阶函数内联到调用站点,在此调用函数还内联了它们的函数自变量,以生成单独的完全优化的代码。
例如,inline
以下fold
函数中的使其速度提高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
请注意,这与inline
大多数其他语言几乎没有相似之处。您可以使用C ++中的模板元编程来实现类似的效果,但是F#也可以在已编译程序集之间内联,因为inline
是通过.NET元数据传递的。
fold
,Array.fold
当应用于复数时,我给出的I比内置函数快3倍。
inline
很多,并且(违反直觉)不是因为它执行内联!
在F#组件的设计准则只提一个关于这个小。我的建议(与那里所说的一致)是:
inline
inline
编写数学库以供其他F#代码使用时,您可能会考虑使用,而您想编写对不同数字数据类型通用的函数。内联和静态成员约束还有许多其他“有趣的”用法,用于类似于C ++模板的“鸭式”场景。我的建议是避免所有类似瘟疫的事件。
@kvb的答案更深入地说明了什么是“静态类型约束”。
inline
关键字也超出数学库是非常有用的。例如,在数据结构的上下文中,可以使用它来由抽象的数据结构构成具体的数据结构,而不会造成任何运行时性能损失。