问题
All my favorite languages have a goto
command. That is, you can create a label, and then later interrupt the flow of the program to go to the label. One of the more useful applications of this construct is to create an infinite loop, like this:
start:
goto start
Unfortunately, if I undertstand the compiler errors correctly, I can't use this same syntax in F#. So, since it doesn't seem to be supported natively, how can I implement the goto
command in F#?
Surely, F# is a powerful enough language to implement a feature as simple as this. Other languages, such as Javascript, which do not natively support goto
, are still able to implement it through a plug-in.
Further, I feel that F#, as one of the languages in the functional programming paradigm, should be able to support higher-level goto
s: where you can pass goto
s to goto
s.
回答1:
A label has a lot in common with a function: they both act as entry points to some code to be executed. Given that similarity, you can do the following:
let goto f = f()
let test() =
let label = (fun () ->
//rad code
)
//tight code
goto label
One minor drawback is having to wrap all your code in closures. I don't know--doesn't seem too bad for getting something as handy as goto
.
回答2:
You can get the behavior of a GOTO in F# with mutually recursive functions. Tail call optimization allows for this goto nature and doesn't push anything onto the stack.
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;
}
Here do_read, do_shift and re_reduce act as labels.
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()
Code source http://sharp-gamedev.blogspot.com/2011/08/forgotten-control-flow-construct.html
回答3:
Unfortunately, if I undertstand the compiler errors correctly, I can't use this same syntax in F#. So, since it doesn't seem to be supported natively, how can I implement the goto command in F#?
As Daniel said, a label and its following instruction block can be translated into a function and its body. Then each goto
becomes a function call. You must pass all local variables as arguments because separate functions have separate scopes and you must add fall-through calls from one instruction block to the next when necessary. However, tail calls are a more general concept.
Your start
loop example becomes:
let rec start () = // .start
start() // goto start
Note that a decent compiler will actually compile this equivalent high-level code back down to jump
/branch
between instruction blocks in assembler. The main difference is that the stack frames must be reorganised because you can tail call between completely dissimilar environments.
Further, I feel that F#, as one of the languages in the functional programming paradigm, should be able to support higher-level gotos: where you can pass gotos to gotos.
Yes indeed. You cannot pass labels around in other languages but you can pass functions around in F#, both as arguments in function calls and as return values from functions. Other languages, like Fortran, do offer computed goto
as a half-way house.
Note that asynchronous programming is an important practical application of this technique. When you call an asynchronous function you tell it where it is to branch to when it completes. For example, when you make a call to start an web page downloading asynchronously you pass it a function that it will invoke once the data is available (essentially, the hardware interrupt received when the last of your data comes in ends up firing off your high-level managed code to operate on the fresh data, which is pretty cool). Modern languages give you the tools to write high-level reusable asynchronous code by combining these goto
-like techniques with extra code generation during compilation. In other languages like C# you are screwed because you want to wrap multiple asynchronous calls in a single try..catch
but you cannot because they are actually spread across many different functions.
回答4:
One approach that is not mentioned in the other answers is to create your own computation builder. I wrote two articles that implement some imperative features for F# in a computation builder imperative { .. }
(read the first one and the second one).
They don't go as far as to implement goto
, but they implement continue
and break
. You could add support for goto
, but you could only jump back to the labels that were executed before. Here is an example using continue
:
imperative {
for x in 1 .. 10 do
if (x % 3 = 0) then do! continue
printfn "number = %d" x }
回答5:
If you take a look at the source code, you'll find that goto.js is implemented as a textual preprocessor of the code. It uses regular expressions to find and replace labels and gotos with proper Javascript constructs.
You can use this approach to extend any language (including F# of course). In .NET you could probably use T4 in a similar way. However, manipulating a language at the textual level is usually more of a hack than a proper extension (goto.js's author himself says "Seriously. Never use this."), this kind of metaprogramming is often instead done by hooking into the language AST.
Surely, F# is a powerful enough language to implement a feature as simple as this.
F# actually has very poor support for metaprogramming. Languages with good metaprogramming capabilities include any Lisp, OCaml (via campl4), Haskell (via Template Haskell), Nemerle, Scala, Boo. Traceur implements proper javascript AST metaprogramming capabilities. But AFAIK there's nothing like this for F# yet.
should be able to support higher-level gotos
GOTOs aren't first-class values in any language I know, so the term "higher-level GOTO" doesn't make sense. However if you're really interested in flow control manipulation in functional languages you should look into continuations.
来源:https://stackoverflow.com/questions/7127417/how-do-you-implement-goto-in-f