你如何在F#中实现goto?

Pet*_*son 6 f# goto

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

 start:
 goto start
Run Code Online (Sandbox Code Playgroud)

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

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

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

Dan*_*iel 11

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

let goto f = f()

let test() =
  let label = (fun () ->
    //rad code
    )
  //tight code
  goto label
Run Code Online (Sandbox Code Playgroud)

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


gra*_*bot 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;
}
Run Code Online (Sandbox Code Playgroud)

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

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


Jon*_*rop 8

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

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

您的start循环示例变为:

let rec start () =  // .start
  start()           // goto start
Run Code Online (Sandbox Code Playgroud)

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

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

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

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


Tom*_*cek 5

其他答案中未提及的一种方法是创建自己的计算构建器.我写了两篇文章,在计算构建器中实现F#的一些命令性功能imperative { .. }(读取第一个第二个).

他们没有尽可能地实施goto,但他们实施continuebreak.您可以添加支持goto,但只能跳回到之前执行的标签.这是一个使用示例continue:

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