你如何在F#中实现goto?

时间:2011-08-19 21:05:40

标签: f# goto

我最喜欢的语言都有goto命令。也就是说,您可以创建一个标签,然后中断程序的流程以转到标签。这个构造的一个更有用的应用是创建一个无限循环,如下所示:

 start:
 goto start

不幸的是,如果我正确地解决了编译器错误,我就不能在F#中使用相同的语法。因此,由于它本身似乎不受支持,我如何在F#中实现goto命令?

当然,F#是一种功能强大的语言,可以实现像这样简单的功能。其他语言(例如Javascript,本身不支持goto)通过插件still able to implement it

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

5 个答案:

答案 0 :(得分:11)

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

let goto f = f()

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

一个小缺点是必须将所有代码包装在闭包中。我不知道 - 对于像goto那样方便的东西似乎并不太糟糕。

答案 1 :(得分:8)

您可以使用相互递归的函数获取F#中GOTO的行为。尾调用优化允许这种goto性质,并且不会将任何东西推到堆栈上。

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

答案 2 :(得分:8)

  

不幸的是,如果我正确地解决了编译器错误,我就不能在F#中使用相同的语法。因此,由于它本身似乎不受支持,我如何在F#中实现goto命令?

正如Daniel所说,标签及其后续指令块可以转换为函数及其主体。然后每个goto成为一个函数调用。必须将所有局部变量作为参数传递,因为单独的函数具有单独的作用域,并且必要时必须将来自一个指令块的直通调用添加到下一个指令块。但是,尾调用是一个更普遍的概念。

您的start循环示例变为:

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

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

  

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

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

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

答案 3 :(得分:5)

其他答案中未提及的一种方法是创建自己的计算构建器。我写了两篇文章,在计算构建器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 }

答案 4 :(得分:1)

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

您可以使用此方法扩展任何语言(当然包括F#)。在.NET中,您可以以类似的方式使用T4。然而,在文本层面操纵一种语言通常更像是一种黑客而不是一种适当的扩展(goto.js的作者自己说“严重。从不使用它。”),这种元编程通常是通过挂钩语言AST完成的。

  

当然,F#是一种功能强大的语言,可以实现像这样简单的功能。

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

  

应该能够支持更高级别的游戏

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