3 ms·
I agree with you completely. I once made a poster (I wish I'd kept it) where I was tracing out the behaviour of a single mega function with around 50 labels in
by CJefferson 5y ago
I agree with you completely.
I once made a poster (I wish I'd kept it) where I was tracing out the behaviour of a single mega function with around 50 labels in it, and gotos all over the place.
I worked on a reimplementation, slowly picking apart the function into subfunctions, while loops, recursive function calls, etc. Took me about 2 weeks.
- munificent 5y agoThat must have been so satisfying once it was done.
- prox 5y agoYou maybe the right person to ask… Aren’t function / functioncallers a form of goto?
- CJefferson 5y agoTechnically yes, all code eventually compiles to goto, or goto equivalents (simplifying slightly) However, the idea behind "goto considered harmful" is almost all code can be written with function, loops and if/else statements and get just as efficient, and much, much easier to understand.
- kazinator 5y agoAll goto code can be mechanically translated into a network of mutually tail-calling functions arranged into an identically shaped call graph that is hardly easier to understand. What you can do with goto-based code is refactor it into a form that obeys certain conventions such that it is easy to mechanically translate to such a tail-call newtork, but then stop short of actually doing it. Then when reading the code, just pretend you're looking at function calls.
- kazinator 5y agoI could refactor a self-contained 50 label goto graph into a network of tail recursing functions pretty quickly; as little as an hour, in the absence of nasty confounding factors. If the goto graph implements a calculation with no side effects like I/O, my network will be all pure functions, too. Completely beyond the reproach of any CS academic. :) There is a mechanical way to do this. 1. We turn all of the function's local variables into function arguments. Each of our tail recursive functions will take these arguments. 2. Every labeled node becomes a function. 3. Every goto becomes a function call invoking the function that its target label was turned into, passing in each of its own argument to the corresponding parameter of that function. The function's return value is immediately returned. 4. Every variable assignment before a goto is turned into an argument expression in the function call which passes the new value to the corresponding argument. E.g. instead a++; d *= 2; goto foo; we have return foo(a + 1, b, c, d * 2, e, ...); Once you have this working, then you refactor. For instance, if you have a function like this: int foo(int a, int b, int c, ..., int z) { return a + c; } which does not pass its arguments to another tail-called function, then you can eliminate all of the arguments and just turn it into: int foo(int a, int c) { return a + c; } and of course edit all of the calls to foo accordingly, and simplify those places also. In this way, you will "discover" simpler functions that work with just a subset of the state transfer.