首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >F#是否使用|> Option.bind进行TCO (尾叫优化)?

F#是否使用|> Option.bind进行TCO (尾叫优化)?
EN

Stack Overflow用户
提问于 2016-01-26 20:57:50
回答 2查看 488关注 0票数 9

这是我的功能:

代码语言:javascript
复制
let rec applyAll rules expr =
  rules
  |> List.fold (fun state rule ->
    match state with
    | Some e ->
      match applyRule rule e with
      | Some newE -> Some newE
      | None -> Some e
    | None -> applyRule rule expr) None
  |> Option.bind (applyAll rules)

它接受一组规则并应用它们,直到尽可能减少输入表达式。我可以将Option.bind重写为一个match表达式,它显然可以利用尾调用优化。但是,这对我来说更优雅,所以我想保持原样,除非它会不必要地消耗堆栈。F#用这个代码做TCO吗?

编辑:这段代码总是不返回任何内容;我将修复它,但我认为这个问题仍然有意义。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-11-22 08:14:11

我将您的代码粘贴到一个文件tco.fs中。我添加了一个applyRule函数以使其可编译。

tco.fs

代码语言:javascript
复制
let applyRule rule exp =
    Some ""

let rec applyAll rules expr =
  rules
  |> List.fold (fun state rule ->
    match state with
    | Some e ->
      match applyRule rule e with
      | Some newE -> Some newE
      | None -> Some e
    | None -> applyRule rule expr) None
  |> Option.bind (applyAll rules)

然后我制作了一个批处理文件来分析IL。

compile_and_dasm.bat

代码语言:javascript
复制
SET ILDASM="C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6 Tools\ildasm.exe"

Fsc tco.fs

%ILDASM% /out=tco.il /NOBAR /tok tco.exe

作为输出,我们找到包含IL的tco.il。相关的功能在这里。

代码语言:javascript
复制
.method /*06000002*/ public static class [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.FSharpOption`1/*01000003*/<!!b> 
      applyAll<a,b>(class [FSharp.Core/*23000002*/]Microsoft.FSharp.Collections.FSharpList`1/*01000008*/<!!a> rules,
                    string expr) cil managed
{
    .custom /*0C000003:0A000003*/ instance void [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.CompilationArgumentCountsAttribute/*01000007*/::.ctor(int32[]) /* 0A000003 */ = ( 01 00 02 00 00 00 01 00 00 00 01 00 00 00 00 00 ) 
    // Code size       26 (0x1a)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  newobj     instance void class Tco/*02000002*//applyAll@13/*02000003*/<!!b,!!a>/*1B000004*/::.ctor(class [FSharp.Core/*23000002*/]Microsoft.FSharp.Collections.FSharpList`1/*01000008*/<!1>) /* 0A000004 */
    IL_0006:  newobj     instance void class Tco/*02000002*//'applyAll@6-1'/*02000004*/<!!a>/*1B000005*/::.ctor() /* 0A000005 */
    IL_000b:  ldnull
    IL_000c:  ldarg.0
    IL_000d:  call       !!1 [FSharp.Core/*23000002*/]Microsoft.FSharp.Collections.ListModule/*01000009*/::Fold<!!0,class [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.FSharpOption`1/*01000003*/<string>>(class [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.FSharpFunc`2/*01000002*/<!!1,class [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.FSharpFunc`2/*01000002*/<!!0,!!1>>,
                                                                                                                                                                                                             !!1,
                                                                                                                                                                                                             class [FSharp.Core/*23000002*/]Microsoft.FSharp.Collections.FSharpList`1/*01000008*/<!!0>) /* 2B000001 */
    IL_0012:  tail.
    IL_0014:  call       class [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.FSharpOption`1/*01000003*/<!!1> [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.OptionModule/*0100000A*/::Bind<string,!!1>(class [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.FSharpFunc`2/*01000002*/<!!0,class [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.FSharpOption`1/*01000003*/<!!1>>,
                                                                                                                                                                                                        class [FSharp.Core/*23000002*/]Microsoft.FSharp.Core.FSharpOption`1/*01000003*/<!!0>) /* 2B000002 */
    IL_0019:  ret
} // end of method Tco::applyAll

我们在这里看到尾部操作码是生成的。这是IL编译器对JIT编译器(实际上生成可执行机器代码)的提示,这里应该可以进行尾调用。

尾调用是否实际执行取决于JIT编译器,可以读取here

票数 1
EN

Stack Overflow用户

发布于 2016-04-29 20:53:37

答案是“不”

正如您所说的,将通过将Option.Bind“扩展”为match表达式来优化调用。这样做将正确地将对applyAll的递归调用放在尾位置。

Option.Bind位于尾部位置的情况下,堆栈将像

代码语言:javascript
复制
+ …
+ applyAll
+ Option.Bind
+ applyAll
+ Option.Bind
_ applyAll

F#编译器不会对此进行优化。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35023814

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档