当你想要返回一个闭包时,我理解嵌套函数的好处。但是当一个函数从未在它定义的函数之外使用时,是否需要保持嵌套的成本?
考虑:
let private validate number =
number > 100
let validateAndPrint (i : int) =
printfn "%i Greater than 100: %s" i ((validate i).ToString())
对战:
let validateAndPrint (i : int) =
let validate number =
number > 100
printfn "%i Greater than 100: %s" i ((validate i).ToString())
这里的问题通常是当我有一些这些内部函数时,我只需要根据执行路径调用其中一个函数(它通常不会禁止将这些函数保密或嵌套并测试排列)。
validateAndPrint
也可以重写为tdd-friendly以接受验证功能:
let validateAndPrint validate i = ...
..但如果validation
的签名包含内部或私人类型,则需要考虑将所有内容公开,并且参数列表会爆炸。
任何重要的差异?
答案 0 :(得分:6)
在ffprobe
模式下,IL是相同的,并且在此特定示例中都内联了Release
的两个版本。
您不能总是依赖内联,例如如果一个函数是"大"然后编译器可以选择不内联它(这对于方法来说是正确的,我不是100%确定这对于F#嵌套函数是正确的。不可能标记它们validate
并强制内联, - 需要咨询F# specs,如果这些函数的行为类似于方法,或者总是在Release中内联)。在调试模式的嵌套情况下,还有额外的FSharpFunc分配和inline
。如果没有嵌套,它只是一个没有分配的静态调用,这是所有可能实现中最便宜的。
所以一般来说,如果嵌套是真正可选的,那么避免使用它会更安全,因为即使函数没有内联,它也总是被称为静态方法。但是,只有当您每秒调用此代码数百万次时才重要。
IL_0019上没有嵌套的静态调用:
callvirt
嵌套,IL_0018上的分配和IL_0020上的callvirt:
.method public static
void validateAndPrint (
int32 i
) cil managed
{
// Method begins at RVA 0x2104
// Code size 51 (0x33)
.maxstack 5
.locals init (
[0] class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32, class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<string, class [FSharp.Core]Microsoft.FSharp.Core.Unit>>,
[1] bool
)
IL_0000: ldstr "%i Greater than 100: %s"
IL_0005: newobj instance void class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`5<class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32, class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<string, class [FSharp.Core]Microsoft.FSharp.Core.Unit>>, class [mscorlib]System.IO.TextWriter, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [mscorlib]System.Tuple`2<int32, string>>::.ctor(string)
IL_000a: call !!0 [FSharp.Core]Microsoft.FSharp.Core.ExtraTopLevelOperators::PrintFormatLine<class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32, class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<string, class [FSharp.Core]Microsoft.FSharp.Core.Unit>>>(class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4<!!0, class [mscorlib]System.IO.TextWriter, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [FSharp.Core]Microsoft.FSharp.Core.Unit>)
IL_000f: stloc.0
IL_0010: nop
IL_0011: ldloc.0
IL_0012: newobj instance void FSSO.Test/validateAndPrint@8::.ctor(class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32, class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<string, class [FSharp.Core]Microsoft.FSharp.Core.Unit>>)
IL_0017: ldarg.0
IL_0018: ldarg.0
IL_0019: call bool FSSO.Test::validate(int32)
IL_001e: stloc.1
IL_001f: ldloca.s 1
IL_0021: constrained. [mscorlib]System.Boolean
IL_0027: callvirt instance string [mscorlib]System.Object::ToString()
IL_002c: call !!0 class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32, string>::InvokeFast<class [FSharp.Core]Microsoft.FSharp.Core.Unit>(class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<!0, class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<!1, !!0>>, !0, !1)
IL_0031: pop
IL_0032: ret
} // end of method Test::validateAndPrint
发布模式,IL_0020的内联比较:
.method public static
void validateAndPrint (
int32 i
) cil managed
{
// Method begins at RVA 0x2050
// Code size 58 (0x3a)
.maxstack 6
.locals init (
[0] class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32, bool> validate,
[1] class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32, class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<string, class [FSharp.Core]Microsoft.FSharp.Core.Unit>>,
[2] bool
)
IL_0000: newobj instance void FSSO.Test2/validate@13::.ctor()
IL_0005: stloc.0
IL_0006: ldstr "%i Greater than 100: %s"
IL_000b: newobj instance void class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`5<class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32, class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<string, class [FSharp.Core]Microsoft.FSharp.Core.Unit>>, class [mscorlib]System.IO.TextWriter, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [mscorlib]System.Tuple`2<int32, string>>::.ctor(string)
IL_0010: call !!0 [FSharp.Core]Microsoft.FSharp.Core.ExtraTopLevelOperators::PrintFormatLine<class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32, class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<string, class [FSharp.Core]Microsoft.FSharp.Core.Unit>>>(class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4<!!0, class [mscorlib]System.IO.TextWriter, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [FSharp.Core]Microsoft.FSharp.Core.Unit>)
IL_0015: stloc.1
IL_0016: nop
IL_0017: ldloc.1
IL_0018: newobj instance void FSSO.Test2/'validateAndPrint@14-2'::.ctor(class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32, class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<string, class [FSharp.Core]Microsoft.FSharp.Core.Unit>>)
IL_001d: ldarg.0
IL_001e: ldloc.0
IL_001f: ldarg.0
IL_0020: callvirt instance !1 class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32, bool>::Invoke(!0)
IL_0025: stloc.2
IL_0026: ldloca.s 2
IL_0028: constrained. [mscorlib]System.Boolean
IL_002e: callvirt instance string [mscorlib]System.Object::ToString()
IL_0033: call !!0 class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32, string>::InvokeFast<class [FSharp.Core]Microsoft.FSharp.Core.Unit>(class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<!0, class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<!1, !!0>>, !0, !1)
IL_0038: pop
IL_0039: ret
} // end of method Test2::validateAndPrint