当嵌套是可选的时,嵌套f#函数与不嵌套它们有何不同?

moa*_*ate 3 f# inline

当你想要返回一个闭包时,我理解嵌套函数的好处.但是当一个函数从未在它定义的函数之外使用时,是否需要保持嵌套的成本?

考虑:

let private validate number = 
    number > 100

let validateAndPrint (i : int) = 
    printfn "%i Greater than 100: %s" i ((validate i).ToString())
Run Code Online (Sandbox Code Playgroud)

与:

let validateAndPrint (i : int) = 
    let validate number = 
        number > 100
    printfn "%i Greater than 100: %s" i ((validate i).ToString())
Run Code Online (Sandbox Code Playgroud)

这里的问题通常是当我有一些这些内部函数时,我只需要根据执行路径调用其中一个函数(通常不会禁止将这些函数保密或嵌套并测试排列).

validateAndPrint 也可以重写更多tdd-friendly以接受验证功能:

let validateAndPrint validate i = ...
Run Code Online (Sandbox Code Playgroud)

..但如果validation签名包含内部或私人类型,则考虑将所有内容公开并且参数列表爆炸.

任何重要的差异?

V.B*_*.B. 6

Release模式中,IL是相同的,并且validate在该特定示例中内联了两个版本.

你不能总是依赖于内联,例如,如果一个函数是"大",那么编译器可以选择不内联它(这对于方法来说是正确的,我不是100%确定这对于F#嵌套函数是正确的.它不可能标记它们inline和强制内联, - 如果这些函数的行为类似于方法或者总是在Release中内联,则需要参考F#规范.在调试模式的嵌套情况下,还有额外的FSharpFunc分配和callvirt.没有嵌套,它只是一个没有分配的静态调用,这是所有可能实现中最便宜的.

所以一般来说,如果嵌套是真正可选的,那么避免使用它会更安全,因为即使函数没有内联,它也总是被称为静态方法.但是,只有当您每秒调用此代码数百万次时才重要.

IL_0019上没有嵌套的静态调用:

.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
Run Code Online (Sandbox Code Playgroud)

IL_0018上的嵌套,分配和IL_0020上的callvirt:

.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
Run Code Online (Sandbox Code Playgroud)

发布模式,IL_0020的内联比较:

.method public static 
    void validateAndPrint (
        int32 i
    ) cil managed 
{
    // Method begins at RVA 0x2050
    // Code size 57 (0x39)
    .maxstack 6
    .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] class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4<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>,
        [2] 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: stloc.1
    IL_000b: call class [mscorlib]System.IO.TextWriter [mscorlib]System.Console::get_Out()
    IL_0010: ldloc.1
    IL_0011: call !!0 [FSharp.Core]Microsoft.FSharp.Core.PrintfModule::PrintFormatLineToTextWriter<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.PrintfFormat`4<!!0, class [mscorlib]System.IO.TextWriter, class [FSharp.Core]Microsoft.FSharp.Core.Unit, class [FSharp.Core]Microsoft.FSharp.Core.Unit>)
    IL_0016: stloc.0
    IL_0017: nop
    IL_0018: ldloc.0
    IL_0019: 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_001e: ldarg.0
    IL_001f: ldarg.0
    IL_0020: ldc.i4.s 100
    IL_0022: cgt
    IL_0024: stloc.2
    IL_0025: ldloca.s 2
    IL_0027: constrained. [mscorlib]System.Boolean
    IL_002d: callvirt instance string [mscorlib]System.Object::ToString()
    IL_0032: 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_0037: pop
    IL_0038: ret
} // end of method Test2::validateAndPrint
Run Code Online (Sandbox Code Playgroud)