如何在F#中实现goto?

如何在F#中实现goto?,f#,goto,F#,Goto,我最喜欢的语言都有一个goto命令。也就是说,您可以创建一个标签,然后稍后中断程序流以转到标签。此构造的一个更有用的应用是创建一个无限循环,如下所示: start: goto start 不幸的是,如果我正确理解了编译器错误,我就不能在F#中使用相同的语法。那么,既然它在本地似乎不受支持,那么如何在F#中实现goto命令呢 当然,F#是一种功能强大的语言,足以实现如此简单的功能。其他语言,如Javascript,本机不支持goto,都是通过插件实现的 此外,我觉得F#作为函数式编程范式中的

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

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

当然,F#是一种功能强大的语言,足以实现如此简单的功能。其他语言,如Javascript,本机不支持
goto
,都是通过插件实现的


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

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

let goto f = f()

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

一个小缺点是必须将所有代码包装在闭包中。我不知道——对于像
goto

这样方便的东西来说,这似乎并不太糟糕。你可以通过相互递归的函数得到F#中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()
代码源

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

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

您的
start
循环示例变为:

let rec start () =  // .start
  start()           // goto start
请注意,一个好的编译器实际上会将这个等价的高级代码编译回汇编程序中指令块之间的
jump
/
branch
。主要区别在于堆栈帧必须重新组织,因为您可以在完全不同的环境之间跟踪调用

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

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

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

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

您可以使用这种方法扩展任何语言(当然包括F#)。在.NET中,您可能也可以使用类似的方法。然而,在文本级别操纵一种语言通常更像是一种黑客行为,而不是一种适当的扩展(goto.js的作者自己说“认真的,永远不要使用它”),这种元编程通常是通过连接到语言AST来完成的

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

F#实际上对元编程的支持非常差。具有良好元编程功能的语言包括任何Lisp、OCaml(via)、Haskell(via)、、。实现适当的javascript AST元编程功能。但对F#来说,现在还没有这样的事

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


GOTO在我所知道的任何语言中都不是一流的价值观,所以术语“更高级别的GOTO”毫无意义。但是,如果您真的对函数式语言中的流控制操作感兴趣,您应该研究。

其他答案中没有提到的一种方法是创建您自己的计算生成器。我写了两篇文章,在计算生成器
命令式{..}
中实现了F#的一些命令式特性(请阅读和)

它们甚至没有实现
goto
,而是实现
continue
break
。您可以添加对转到的支持,但只能跳回以前执行的标签。下面是一个使用
continue
的示例:

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

顺便问一下:这是个玩笑吗?我看着你的代表,你肯定知道!我和你一样--跳进嵌套闭包的中间听起来很有趣!唉,
goto
甚至不能用于将来。我担心没有计划来实现这个急需的功能。兰姆达:我感觉这里有个笑话…我喜欢这个答案。。。但你应该说“我