我最喜欢的语言都有一个goto
命令.也就是说,您可以创建一个标签,然后中断程序的流程以转到标签.这个构造的一个更有用的应用是创建一个无限循环,如下所示:
start:
goto start
Run Code Online (Sandbox Code Playgroud)
不幸的是,如果我正确地解决了编译错误,我就不能在F#中使用相同的语法.因此,由于它本身似乎不受支持,我如何goto
在F#中实现该命令?
当然,F#是一种功能强大的语言,可以实现像这样简单的功能.其他语言(如Javascript)本身不支持goto
,仍然可以通过插件实现它.
此外,我觉得F#作为函数式编程范例中的一种语言,应该能够支持更高级别的goto
s:你可以将goto
s 传递给goto
s.
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
.
您可以使用相互递归的函数获取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
不幸的是,如果我正确地解决了编译错误,我就不能在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
但不能,因为它们实际上分布在许多不同的函数中.
其他答案中未提及的一种方法是创建自己的计算构建器.我写了两篇文章,在计算构建器中实现F#的一些命令性功能imperative { .. }
(读取第一个和第二个).
他们没有尽可能地实施goto
,但他们实施continue
和break
.您可以添加支持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)
归档时间: |
|
查看次数: |
1287 次 |
最近记录: |