【问题标题】:How do you implement goto in F#?你如何在 F# 中实现 goto?
【发布时间】:2011-08-24 23:56:01
【问题描述】:

我最喜欢的所有语言都有一个goto 命令。也就是你可以创建一个标签,然后再打断程序的流程去到这个标签。这种结构更有用的应用之一是创建一个无限循环,如下所示:

 start:
 goto start

不幸的是,如果我正确理解编译器错误,我就不能在 F# 中使用相同的语法。那么,既然似乎原生不支持,那如何在 F# 中实现goto 命令呢?

毫无疑问,F# 是一种足够强大的语言,可以实现如此简单的功能。其他语言,比如Javascript,原生不支持goto,通过插件实现still able to implement it

另外,我觉得F#作为函数式编程范式中的一种语言,应该可以支持更高级别的gotos:可以将gotos传递给gotos。

【问题讨论】:

  • 顺便说一句:这是在开玩笑吗?我看看你的代表,你肯定知道这一点!
  • 我和你在一起——跳到嵌套闭包的中间听起来很有趣!
  • 唉,goto 甚至不是 reserved 供将来使用。我担心没有计划实施这个急需的功能。
  • 我觉得这里有一个笑话......

标签: f# goto


【解决方案1】:

标签与函数有很多共同点:它们都充当要执行的某些代码的入口点。鉴于这种相似性,您可以执行以下操作:

let goto f = f()

let test() =
  let label = (fun () ->
    //rad code
    )
  //tight code
  goto label

一个小缺点是必须将所有代码包装在闭包中。我不知道——获得像goto 这样方便的东西似乎还不错。

【讨论】:

  • 我喜欢这个答案......但你应该说“让 rec 测试......”毕竟我们想在块内调用 goto
  • 为什么goto需要调用自己?
  • @Daniel 我很好奇。您将如何使用您提供的这种模式转换我给出的 c goto 示例?
  • @gradbot:你只需用gotogoto do_read 等包装所有函数调用。我的回答很蹩脚。 goto 所做的只是调用一个函数。但它的语法类似于 OP 想要的(我认为)。
【解决方案2】:

您可以使用相互递归的函数在 F# 中获得 GOTO 的行为。尾调用优化允许这种 go​​to 特性,并且不会将任何内容压入堆栈。

int parse() 
{
    Token   tok;

reading:
    tok = gettoken();
    if (tok == END)
        return ACCEPT;
shifting:
    if (shift(tok))
        goto reading;
reducing:
    if (reduce(tok))
        goto shifting;
    return ERROR;
}

这里 do_read、do_shift 和 re_reduce 充当标签。

type Token = END | SOMETHINGELSE

type Status = ACCEPT | ERROR

let parse gettoken shift reduce () =
    let rec do_read() =
        match gettoken() with
        | END -> ACCEPT
        | _ as tok -> do_shift tok

    and do_shift tok =
        if shift tok then
            do_read()
        else
            do_reduce tok

    and do_reduce tok =
        if reduce tok then
            do_shift tok
        else
            ERROR

    do_read()

代码源http://sharp-gamedev.blogspot.com/2011/08/forgotten-control-flow-construct.html

【讨论】:

  • 正如我在(开玩笑的)回答中指出的那样:标签和函数调用之间确实没有太大区别。最大的区别可能是失败......我不知道如何在 F# 中实现。当然,您的方法有效,因为每个函数都以函数调用结束。很好的答案。
  • @Daniel:失败只是在前一个函数的末尾尾部调用下一个函数。
【解决方案3】:

不幸的是,如果我正确理解编译器错误,我就不能在 F# 中使用相同的语法。那么,既然似乎原生不支持,那我该如何在F#中实现goto命令呢?

正如丹尼尔所说,一个标签及其后面的指令块可以翻译成一个函数及其主体。然后每个goto 成为一个函数调用。您必须将所有局部变量作为参数传递,因为单独的函数具有单独的范围,并且您必须在必要时从一个指令块添加到下一个指令块的直通调用。但是,尾调用是一个更通用的概念。

您的start 循环示例变为:

let rec start () =  // .start
  start()           // goto start

请注意,一个体面的编译器实际上会将这个等效的高级代码编译回汇编程序中指令块之间的jump/branch。主要区别在于堆栈帧必须重新组织,因为您可以在完全不同的环境之间进行尾调用。

此外,我觉得 F# 作为函数式编程范式中的一种语言,应该能够支持更高级别的 goto:您可以将 goto 传递给 goto。

确实如此。您不能在其他语言中传递标签,但可以在 F# 中传递函数,既可以作为函数调用中的参数,也可以作为函数的返回值。其他语言,如 Fortran,确实提供计算 goto 作为中途站。

请注意,异步编程是该技术的重要实际应用。当你调用一个异步函数时,你告诉它在它完成时分支到哪里。例如,当您调用开始异步下载网页时,您向它传递了一个函数,一旦数据可用,它将调用该函数(本质上,当您的最后一个数据进来时收到的硬件中断最终会触发您的高级托管代码来操作新数据,这非常酷)。现代语言通过将这些类似goto 的技术与编译期间的额外代码生成相结合,为您提供了编写高级可重用异步代码的工具。在 C# 等其他语言中,您会被搞砸,因为您想将多个异步调用包装在单个 try..catch 中,但您不能,因为它们实际上分布在许多不同的函数中。

【讨论】:

    【解决方案4】:

    其他答案中未提及的一种方法是创建自己的计算构建器。我写了两篇文章,在计算构建器imperative { .. } 中为 F# 实现了一些命令式特性(阅读 first onesecond one)。

    他们没有实现goto,但他们实现了continuebreak。您可以添加对goto 的支持,但您只能返回 跳转到之前执行的标签。这是一个使用continue的例子:

    imperative { 
      for x in 1 .. 10 do 
        if (x % 3 = 0) then do! continue
        printfn "number = %d" x }
    

    【讨论】:

      【解决方案5】:

      如果您查看source code,您会发现 goto.js 是作为代码的文本预处理器实现的。它使用正则表达式来查找和替换带有适当 Javascript 结构的标签和 goto。

      您可以使用这种方法来扩展任何语言(当然包括 F#)。在 .NET 中,您可能会以类似的方式使用 T4。但是,在文本级别操作语言通常更像是一种 hack,而不是适当的扩展(goto.js 的作者自己说“说真的。永远不要使用这个。”),这种元编程通常是通过挂钩语言 AST 来完成的.

      当然,F# 是一种强大到足以实现如此简单功能的语言。

      F# 实际上对元编程的支持很差。具有良好元编程能力的语言包括任何 Lisp、OCaml(通过campl4)、Haskell(通过Template Haskell)、NemerleScalaBooTraceur 实现了适当的 javascript AST 元编程功能。但是 AFAIK 对于 F# 来说还没有这样的东西。

      应该能够支持更高级别的 goto

      GOTO 在我所知道的任何语言中都不是一流的值,因此“更高级别的 GOTO”一词没有意义。但是,如果您真的对函数式语言中的流控制操作感兴趣,您应该查看continuations

      【讨论】:

      • 我认为当您编写 F#“对元编程的支持非常差”时,您对 F# 有点苛刻。自定义工作流程、报价以及某种程度的类型提供程序等功能可能不是最终的元编程工具,但在 IMO 中,它们应该得到的不仅仅是被描述为“非常差”!
      • @Joh :我不会将自定义工作流程描述为元编程。与我为其他语言描述的其他元编程工具相比,引用甚至类型提供程序仍然很差...... Ramon Snir 的宏项目与它们相似,这就是它如此酷的原因。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-04
      • 2012-09-13
      • 1970-01-01
      相关资源
      最近更新 更多