Developer Argues For 'Forgotten Code Constructs' Like GOTO and Eval (techbeacon.com)
mikeatTB quotes TechBeacon:
Some things in the programming world are so easy to misuse that most people prefer to never use them at all. These are the programming equivalent of a flamethrower... [But] creative use of features such as goto, multiple inheritance, eval, and recursion may be just the right solution for experienced developers when used in the right situation. Is it time to resurrect these four forgotten code constructs?
The article notes that the Linux kernel uses goto statements, and links to Linus Torvalds' defense of them. ("Any if-statement is a goto. As are all structured loops...") And it points out that eval statements are supported by JavaScript, Python, PHP, and Ruby. But when the article describes recursion as "more forgotten than forbidden," it begs the inevitable question. Are you using these "forgotten code constructs" -- and should you be?
The article notes that the Linux kernel uses goto statements, and links to Linus Torvalds' defense of them. ("Any if-statement is a goto. As are all structured loops...") And it points out that eval statements are supported by JavaScript, Python, PHP, and Ruby. But when the article describes recursion as "more forgotten than forbidden," it begs the inevitable question. Are you using these "forgotten code constructs" -- and should you be?
Honest question: Am I not supposed to use recursion? Am I missing something?
Just use the one with the more clearer intent, unless it really affects performance.
Just keep in mind that familiarity is not the same as clarity.
Those who do not learn from commit history are doomed to regress it.
I use JMP's if I'm messing around on some old micros, does that count?
How is goto return better than just return?
http://michaelsmith.id.au
Not sure about the idea of recursion being forgotten, but I used to work on a system (GEC 4000 series) which had no stack, making recursion slightly more difficult. The neat way of achieving it was by using a goto, back to the beginning of the function.
Recursion is an easy way to implement solutions to a number of problems. But if you don't have a clearly finite depth then it can be dangerous. There is often a way to use a loop that doesn't pile on the stack the way recursion can.
That said, it doesn't seem like it belongs in this list.
Frankly, it doesn't seem like a great article. Yup, those things can be misused. Yup, if something can be misused, it will be. I use ruby, so I have access to at least 3/4 of these dark techs. Whatever.
Well yeah the complex stuff should be in user land, as with Minix. But C is just an assembler language. There can't be may things you need to do in a kernel which can't be done in C.
http://michaelsmith.id.au
IMHO just for cleanliness, and can help with debug later. It allows you to have a single exit point to a function, so in that case if you want extra debug/logging/whatever as to why its exiting at that point you can add it right in the goto instead of having to go to every return statement. Even though your most likely going to have logging at those error points anyway it provides one central location if you need to debug something
Also forgot to add to this, memory cleanup is another big one. Instead of having to free in every single possible 'if error return' block you can have it always do a check and free in the goto
History lesson.
https://en.wikipedia.org/wiki/Considered_harmful
GOTO considered harmful, raised out of a generation of BASIC programmers that knew only too well that they were horrible to deal with. Early micros had RENUM so you could move line numbers around and attempt to preserve GOTOs. They were awful, but only on 8-bit micros.
Later C used them in local jump structures using LABEL: which wasn't even remotely as bad as BASIC. Everyone is allergic to GOTO from BASIC so the whole idea got canned along with it - baby out with the bath water. This is why we say "GOTO considered harmful, considered harmful". The idea that a code construct is so repulsive that we've condemned it to never be used again.
GOTO is useful. Certain forms of C exception handling code benefit from GOTO immensely. They make the code both more readable and more performant. Unfortunately we can't submit this code because in a code review...."GOTO considered harmful" circa 1990. Brainless dogma has won over thought. I've seen generations of programmers that would never consider GOTO to be a valid keyword. They won't consider it on the basis of a decades-old argument that was meant for a different language in a different age. As much as I might be right I won't pass a code review, so I don't use it.
People seem happy to hate on goto while using other things that are goto in all but name (break, continue, return, throw). Throw/catch is particularly useful as a non-local goto in places where you want to catch some kind of condition (usually an error) that can be detected in lots of places. C++ RAII eliminates most of the need for local gotos though - you can make local classes with destructors for cleanup, with the added benefit that it's exception-safe.
The whole point of recursion is to use the stack. If not some sort of while loop is probably called for.
http://michaelsmith.id.au
How is goto return better than just return?
In C in particular, which is the ONLY place I'll use goto... i might have a pattern like something like...
{
a = malloc(something)
if malloc failed goto e1
b = malloc(something2)
if malloc failed goto e2
c = malloc(onemorething)
if malloc failed goto e3
open a file... if error happened goto e3
some other error happened goto e3
e3:
free (a)
e2:
free(b)
e3:
return;
}
The goto sequence cleanly handles the memory free. Obviously you wouldn't want to just return after the 3rd malloc failed.
Even if you have code that lives in the same address space as the kernel, it isn't necessarily part of the kernel. An optional set of modules is fine to load in the same address space if your architecture is one of the many that are bad at context switching between user and supervisor modes. Usually it's cache flushing related to page table updates that what burns you.
C is definitely not assembler language. Maybe it was before ANSI got their hands on it. Most of the core routines that I consider important to kernel development cannot be done very effectively in C. The implementation is often long, tedious and inefficient when done in C. Saving and restoring contexts from an interrupt handler on ARM is a fine example, you need to embed some rather specific instructions to accomplish this. And sadly gcc still has subtle bugs when generating ISR returns if you do something silly like turn optimizations off.
A small boot loader for a factory diags project took me about 1500 lines of C, but a little under 300 lines of assembler. That should be pretty shocking given that assembler tends to be pretty lengthy. The C had to do a lot of advanced gymnastics that my coworkers had trouble understanding and frequently would break, the assembler is nothing special and is pretty serviceable once there are sufficient comments. But full disclosure, my coworkers didn't bother modifying my assembler version of the boot loader. Because once it gets to a certain point it is able to load arbitrary binary blobs and execute those in a very DOS .COM file like way. The design was patterned more after CP/M than DOS, but on 64-bit ARMv8 instead of 16-bit x86 or 8-bit 8080.
It depends.
In some cases, you want to allow goto statements, for instance because they help manage failure handling without adding condition or exception constructs.
In some case, you want none of these gotos, because you are using processes or tools which are (partly or entirely) not compatible with them, and you need these tools to work more than you need gotos.
In some cases, you don't want recursivity because the contex does not favor them (think embedded SW with restricted stack size).
In some cases you want recursion because it makes code simpler and closer to the principles behind it, thus more maintainable.
In some cases, you want class-like constructs in C be don't want C++ because the legacy code, people involved, time alloted, or general context just does not allow you to rewrite the whole thing.
Etc.
That's not a reason to outlaw their use. If all you have are mediocre developers then no amount of coding standards will help you.
Less experienced developers can learn from good code. Sure they will get it wrong as they find their feet - we have code reviews to deal with that - but we shouldn't seek to handicap them by taking away tools they can use to express themselves.
Do what you want with your GOTOs, but do not bring ALTER back.
It gives you a chance to unwind something (like taking a lock) before the return.
Somebody will publish a paper entitled: "Class statement considered harmful." and he will be applauded as the new IT guru!
char *a = NULL;
a = malloc(something);
if (a) free(a);
and so on.
http://michaelsmith.id.au
Gotos in C are like operator overloading in C++. It's a handy mechanism when used judiciously, in very specific circumstances. If GOTO is abused, used to jump all over the place (which it can literally do), yeah, of course it's bad. But then again, you can always name a function:
int AddTwoNumbers(int x, int y);
when it actually multiplies two numbers. Does this mean named functions are bad, because they could be misused? Why would you purposefully abuse the language like that?
Generally speaking, the rule of thumb with GOTO seems to be to use it in exceptional circumstances, such as error handling. I don't see anything wrong with that. Honestly, it would be better if there were a more specific and constrained mechanism for doing that, but you use the tools you have.
Irony: Agile development has too much intertia to be abandoned now.
Just like Linus, you seem to fail to understand the problem. Dijkstra argued against *unstructured* jumping around, since this made programs very hard to understand (look up some source from that era to get an idea of what he was arguing against. It wasn't just a single goto here or there, it was 'using goto for everything we now use structured constructs for, like loops, switch-statements, etc.). Dijkstra argued for replacing those goto's with structured jumps as much as possible. And guess what? By and large, the software world has done so, and become much better for it.
I very much doubt he meant for his statement to become dogma in the way it has, and he certainly wasn't arguing for the complete removal of all forms of flow control, structured or not (as you and Linus seem to think). Goto, like everything else, is a tool. It has its place. You should not use it if a better tool is available, but you should also feel free to use it if it is the best you have. And the fact that assembly _only_ has goto is immaterial. The whole point is to allow reasoning over the language in the language itself.
Dijkstra always struck me as a sensible, practical man. He wrote about an argument he had about driving printers. In his era, printers could only accept a character once every so often (because they were slow, mechanical beasts, without much in the way of buffering), so his colleagues wanted to intersperse printing code with other processing. Dijkstra didn't like this, and wanted to print using an interrupt that would signal when the printer needed a new character. His colleagues fought against that: not only were interrupts more costly than just interleaving printer output with normal code, but Dijkstra was 'throwing away' valuable information about printer timing that could be used to improve efficiency!
His colleagues were, of course, completely right - right up until the moment when the hardware changed, and their programs no longer worked, that is...
The article talks about 4 features: goto, eval (run code from a string), multiple inheritance, and recursion. It discusses why the 4 get attacked by simplicity advocates:
goto -- incomprehensible logic in programs
eval -- security risks
multiple inheritance -- breaks single responsibility since one module can have subtle impacts on how other modules acts in this context
recursion -- article isn't clear though the comments above are mostly correct. In non-tail recursive languages recursion usually creates algorithms that are O(n) in memory. Even in tail recursive languages this can happen (and in fact in those languages because more complex recursions are encourages O(n^2) isn't uncommon when recursion isn't used carefully / well understood).
It then mentions that these things should be used to avoid complexity in certain situations.
goto -- error handling
multiple inheritance -- is generally too useful to give up. implement with interfaces and be careful
eval -- JSON, HTML, math...
recursion -- trees, some list algorithms... recommend to implement imperative style mostly though (article assumes the language can't handle recursion)
Now my opinion:
Recursion is obviously the best understood of the 4. It is easily provable that there exists recursive algorithms which are both important and are not implementable as loops. Recursion classification is a still active research problem. Most imperative programers don't even bother to think deeply about their algorithms and not using design patterns from recursive features means the same bugs are introduced over and over again in code. IMHO there is no reason not to be abstracting loops away using built in functional design patterns in code.
Multiple inheritance is too powerful to give up. Java was wrong here. Better safety than the C++ style seems to be needed though. For OO languages this should be an active area of experimentation.
goto is today rarely used and when it is it often avoids complexity. I think we hit the right level of compromise here decades ago and this is a dead issue.
Eval I think history has shown that without explicit evals developers end up having to create implicit evals where the code acts in complex ways on input. The code / data duality is not dead. Complex evaluation of input and layering aren't going away. Perl's concept of taint checking is likely the best approach: make it explicit and let the compiler check for accidental security risks.
People who hate goto but love exceptions drive me up the wall. You hate the cute little goto with the well defined label you can instantly fly to in any IDE from vi on up, but you love the goto that jumps across any number of functions and puts you in some code (std:terminate if you fucked up!) ? And don't forget that in C++ (and others I think), you aren't even sure if a given function can throw an exception except by inspection.
Worst. Flow control. Evar.
Eval has been part of the Lisp programming language since the dawn of computing, but few experienced Lisp programmers ever used it outside of read-eval-print loops and software development tools (where its existence is a totally awesome gift). Eval is widely considered a beginner's trap. It is rarely "the right thing," typically because it is super inefficient. But my biggest concern is that it is a huge security hole, especially when the expression to be evaluated comes from the user. Think SQL injection attack. It is wise to require some sort of security credentials and/or filters to make sure the expression is within a "safe subset" of the language... at which point you might as well write the mini-interpreter you really need, instead of making the entire, underlying language and the application's entire data environment available for anyone to use or misuse.
I believe you are missing the greater-than character, which also looks a bit like a letter "V" with the point facing to the right. Here's one now: >
Oh, and the last time I worked with an 80-column screen was probably 30 years ago. My current screen comfortably displays at least double that number. I really don't see why we stick with that tired old convention.
Any "dirty" style should be avoided as a rule and applied with good reasoning.
My guilty pleasure is to return multiple return statements. The reasoning behind this is that it sometimes makes code better readable. Less nesting/fewer methods. Translating "avoid multiple return statements" to loops would result in avoiding break and continue statements.
If one's self-critical enough, code will be cleaned up eventually. Cleanly avoiding multiple return statements -and too many avoiding break and continue statements- usually comes with a breakthrough in the functional decomposition of the problem at hand.
I hadn't the slightest objection to his spending his time planning massacres for the bourgeoisie... (P.G. Wodehouse)
You aren't going to win that debate easily. You are arguing for micro kernels. And of course you are right that micro kernels do allow for extensions much more easily. The problem is that they have notoriously horrible performance for operations that repeat too frequently because the wrapping and unwrapping of calls gets too expensive. Same thing that happens in user code at a high level. Linear efficiency matters. Speeding up every computer on the planet 20% is worth a lot of hassle to OS developers.
You are wrong here. The whole point of recursion is to abstract away the details of how a loop executes and focus on what the loop does. Then later you use a recursion design pattern to abstract away the naive recursion. In a looping structure you end up having to decide immediately on bounds cases handling.
Most languages that handle recursion well just translate most simple recursions into iterative loops during compilation anyway.
The point is that you have several mallocs to deal with, and more than one error condition (not just failing the malloc itself).
So if you make 3 mallocs, and then there is an error opening the file... you still have to free all 3 mallocs.
the use of 3 if(x) free(x) instead of multiple goto labels is fine, but you still need goto to get to the cleanup block from the various error points.
lol. on the upside it won't compile so no one will ever suffer them. :p
Yes, goto is useful for exception handling, in languages that lack specific exception handling constructs, but have goto. This is a good reason to use goto in such languages, but I think it ultimately points to a deficiency in the language, rather than an argument in favour of goto as a language construct.
IMHO, to argue for goto as a language construct, it is not enough to argue that there are a small number of specific well-defined ways in which it is reasonable to use goto in languages that lack control-flow constructs for particular purposes. This would better support an argument for the availability of suitable constructs for these purposes. Ultimately, to argue for goto as a language construct, I think it would be necessary to claim that is reasonable to use goto in ways that defy classification. AFAIK, this claim is seldom made.
The problem with microkernels is not the wrapping and unwrapping of calls, or the cost of switching, or copying memory. The real problem is that different tasks in the microkernel do not have a synchronized view of the system state. As soon as you pass a message where you copy parts of the state, you have two different copies of that state, and as one task changes their copy, the other becomes outdated. Managing all of this becomes more complex than having a big kernel where different tasks share the same memory structures.
> I very much doubt he meant for his statement to become dogma in the way it has
The original title Dijkstra gave his paper is 'A Case Against the Goto Statement'. It was a CACM editor who came up with the Considered Harmful version.
You've missed my point. The people I have a problem with are people who will avoid a goto at all costs, even doing things like this so they can say they didn't use a goto:
do { ... ...
if (exit_cond) break;
} while (0);
I really have seen that done in a popular piece of open source software. If a goto really is the cleanest way to achieve what you need to do, then just use a goto. Don't write code that does a goto while using some convoluted means to avoid the goto keyword.
That said, in C++ there are many situations where you can do better than use a goto. For example cleaning up on error is better done with destructors as it will be exception-safe, and exceptions provide a structured goto that only lets you jump out, but lets you do so across multiple frames.
You should read "GOTO considered harmful" before you bash it.
"Most programmers have heard the adage "Never use goto statements", but few of today's computer science students have the benefit of the historical context in which Dijkstra made his declaration against them. Modern programming dogma has embraced the myth that the goto statement is evil, but it is enlightening to read the original tract and realize that this dogmatic belief entirely misses the point."
http://david.tribble.com/text/...
In the bad old days, all you had was goto, and every program looked like spaghetti. Now that we have if...then...else, loops, switch-case statements,
goto should only be used as a last resort (and every use should be justified). I've been a professional programmer for twenty years; last year I used goto *twice*.
And never forget https://xkcd.com/292/
Pretty sure you're doing a lot better than the jackass who came up with the phrase "Forgotten code construct".
Recursion is either so easy it is more of a trick than a useful tool, or it is more complex in which case it is generally much harder to get the terminal conditions right then it is to avoid recursion.
I remember when I learned C, digging through K&R to find how to do goto. I also remember learning ruby and not even thinking about whether or not goto was supported. It really isn't needed and as Dikstra pointed out is mostly used for creating sloppy code.
it depends on the system environment , if your writing code to be used by a multi-thread o/s environment where use is discuraged as fearlessly as when you writing stand alone code for a embeded stand alone system where use of such can be expected , the other is the use of global vars
Sometimes code has to do multiple things and if any of those things fail, the whole lot has to be cleaned up. Using a "goto cleanup" is usually a lot more preferable way of cleaning up that nesting the code in conditionals rendering it unreadable or duplicating the cleanup in several places.
Try writing an assembly language program without using jump.... what is that, if not goto? Elevating the non use of GOTO to the level of a religion is just as crazy as any other religion. There's a right way and a wrong way to use it, just like there's a right way and a wrong way to use a pair of scissors. Safety scissors are smart in pre-school but an adult is expected to use regular scissors without killing themselves. Is your programming at infant or adult level?
Seven puppies were harmed during the making of this post.
When writing C code where exceptions are not available, I'll often use goto statements to perform error handling. Its useful because you only have to write any necessary memory de-allocation code once. Its pretty easy to simulate a try/finally block using them and its a way better and more readable way to write that sort of code then the other alternatives you have in C. All that said, I prefer exceptions, they more or less get the same job done, its easier to nest them, and IMHO are a bit more readable than the goto method. Only downside of exceptions is the execution overhead is often very bad compared to a goto.
https://ask.slashdot.org/story...
The problem with eval() is that it's incredible power can't be used safely in most languages, as whatever you eval() becomes part of the main program. If you could eval() code in a sandbox and apply memory and CPU cycle limits to it, it would be very useful, but most languages don't have such features making eval() little more than a dangerous toy that sometimes becomes useful for debugging.
As for goto, the Linux kernel is full of it and quite readable because of it. But outside of C you tend to have other mechanism to deal with resource cleanup that make it no longer necessary (RAII in C++, 'with' statements in Python).
Recursion again depends heavily on the details. In Python you quickly run into stack overflows when you use recursion to much (1000 is the default), so it's best avoided. In Haskell or Scheme it's the normal way of doing things, thanks to tail recursion support.
Multi-inheritance I never found very useful, even single-inheritance is rarely a good solution and you are better of doing composition most of the time.
Goto is bad.
Goto is bad for people who can't code and people who can't read code. To consider a construct bad is to not understand it.
This is an age old debate. It comes down to this: monolithic kernels tend to be faster so they are in common use.
Recursion has never been dead.
Tail recursion is an essential part of all modern efficiency in compilation.
Many languages, especially functional languages, cannot really even begin without recursion.
If you haven't got what I am saying, read this comment from the beginning.
This comment was written with the intention to opt out of advertising.
{
char *a = NULL, *b = NULL, *c = NULL;
FILE *f = NULL;
if (!(a = malloc(something)))
goto fail;
if (!(b = malloc(something)))
goto fail;
if (!(c = malloc(something)))
goto fail;
if (!fopen(f))
goto fail;
some code
return 0;
fail:
if (f)
fclose(f);
free(c);
free(b);
free(a);
return -1;
}
FTFY. mkay?
CLI paste? paste.pr0.tips!
In my opinion there are valid uses of GOTO where it could be used, but nowhere else, especially in C.
Modern languages should preferably have constructs especially for those cases so that you wouldn't have to use GOTO, but many languages don't.
The article already mentions error handling in languages without RAII or exceptions, where resources have to be deallocated. Note also that not all resources are objects on the heap: The function may need to close a file or a network connection etc.
I find goto most useful for for breaking out of loops. Many languages have constructs especially for breaking out of nested loops to a scope that encloses the enclosing loop but there is one class of loop that is rarely supported: Loops for looking up an item in a data structure, you want to take a different code paths for when an item has been found to when the loop has run its course without a result. ... break ... else: ..., where the else-block is taken only if you did not break out of the loop.
The only major language I know of that supports this with its own construct is Python in the form: for:
In languages that allow nested break statements you could emulate that by enclosing the loop within an outer loop and breaking out of that but IMHO that would be even less readable than using GOTOs.
"We mustn't be caught by surprise by our own advancing technology" -- Aldous Huxley
It is rarely "the right thing," typically because it is super inefficient.
In compiling implementations of programming languages, it's anything but inefficient.
But my biggest concern is that it is a huge security hole, especially when the expression to be evaluated comes from the user.
One of my favourite quotes in programming: "If you don't want to do something, just don't do that."
at which point you might as well write the mini-interpreter you really need,
Will you also rewrite the compiler, and the memory manager, and write a set of interfaces between your two separate kingdoms?
Ezekiel 23:20
yeah except you've got e3 twice so it wouldn't even compile
Not only faster, they are also simpler to get right, for everything except trivial cases.
Eval, properly used, can be extremely valuable and reduce a lot of code bulk. Improperly used it presents various nightmare scenarios for security and stability.
In reality though, I don't see much more than a slightly nuanced difference between using eval and dynamically constructing any portion of a SQL statement for execution at runtime.
Use it, but use it carefully.
Suppose you were an idiot. And suppose you were a member of congress. But then I repeat myself. -- Mark Twain
Goto: A way to enable lousy programmers to write impenetrable code. Are there extremely unusual circumstances, where a superstar might use a Goto in a good way? Yes, but the price - encouraging use by the incompetent - is not worth it.
Multiple inheritance: Middle ground. In a few circumstances useful, but the conceptual complexity is too high for many programmers. On the other hand, those will not be the ones designing your architecture. Mixed feelings about this one.
Recursion: Many algorithms can be implemented more cleanly with recursion than with iteration. If recursion were better supported, it would be more widely used. Unfortunately, the most widely used languages have poor implementations (C# and Java, to name two), making recursion horribly inefficient. Optimizing for tail-recursion is not hard (Scala does it on the JVM), so it's weird that this isn't done in all modern languages.
Enjoy life! This is not a dress rehearsal.
Most language compilers are using goto constructs under the covers already. Disassemble some MSIL, for example, and you'll see how there are some in every single if-else or switch-case block.
I'm tired of this mentality. I'd rather we favored those with skill rather than those with a lack of it. We as a people would go much farther.
Do you cripple the use of bicycles by forcing everyone to ride with training wheels? Or do we in fact favor those who can ride and instead burden the new-comer with the difficulties around obtaining and installing training wheels on very poor low-end bicycles?
Why should coding be any different? Sometimes people craft very complex and difficult pieces of software that tie together more than 20 libraries all which have their own quirks. I need the ability to share raw pointers, I need the ability to avoid ref-counting or shared_ptrs. I need to sometimes work with systems that have their own scheduler (Erlang, cough) and then bind C libraries into that ecosystem which doesn't allow blocking for more than 1ms. So I need crazy thread logic sometimes and odd code to support linking two separate mutex idioms from 2 different libraries so the lock works across the boundry....
Sometimes I just wish to be left alone in a complex space where another soul's mere presence is essentially proof of their abilities and understanding of logic. Similar to how adults sometimes wish to leave behind children and mingle only with other mature adults, I desire this of a programming language. Something to scare away all the posers and poor misguided (but righteous and well meaning) individuals.... It's not elitist thinking just like Adults aren't really being rude when trying to mingle with other like-minded adults.... it's more of a time-saver for people who find that many "adults" are actually children in disguise and only after 30 minutes of talking can you determine they are fake. I grow tired of wasting my time and eventually wish to move to a place where it's harder for the fake to blend in. It was amazing going from finding 1-2 good people every 50 to instead finding 1-2 good every 10.
For me that language has been C++ and simply put it's the most amazing thing I've ever discovered in my programming career. I also love how everyone still is scared to death of it and clamoring for it's deprecation while simultaneously using programs written in C++ to post these complaints.
If you think a certain construct is somehow inherently bad, you are a shit coder. There is NOTHING wrong with goto, multiple inheritance, etc. They all have there uses. If you think they are bad. STOP. I deal with idiots like you all the god damned day, and I'm sick of it.
I think some time in the misty past (1970s?) recursion went through a fad phase, and it was hailed as the solution to every programming woe, not to mention the secret key to artificial intelligence. I can remember studying Logo (which is a variant of LISP) at one time. Logo composed every function call recursively: when it hit a key word that required arguments, then it would put that on hold and go looking for those arguments, some of which might be keywords that required their own arguments, etc. That's not unique among programming languages, but the syntax provided no clues or organization: no parenthesis, no brackets, no braces, just a string of words, and the only way to figure out which was an argument to what was if you already knew (or stopped to look up!) how many arguments each word takes. But supposedly you wouldn't need help reading it because it's recursive, and recursion is wonderful magic.
Incidentally, Forth suffered from a similar readability problem, but at least it executed way way way faster.
The other thing I remember about Logo and recursion was the textbooks and tutorials trying to teach me how every loop could be done using recursion -- and should be! Why would you do that? Because it's the Logo Way, of course. And because recursion is wonderful magic.
It was overly complex and inefficient, to be sure. However. . . I happily use recursion for actually recursive tasks, such as traversing various kinds of tree structures.
Depends on their usage of goto... using it to jump backwards, or make loops, or basically any other usage, yea, those should be stricken
So does that mean in C it's safe to use goto so long as I make sure I don't purposely or accidentally create recursion with it? ;-)
'The Economy' is a giant Ponzi scheme whose most pitiable suckers are the youngest among us and the yet-unborn.
Here's a much better pattern for you, in a c-ish form:
How to manage iffy resources in a structured manner
You can generalize that pattern into almost any situation and it will work well. If you need details, then instead of true/false, pass can be a value or a bit mask, etc. and then the check at the end can be verbose about what exactly went wrong. Essentially still the same pattern.
I've fallen off your lawn, and I can't get up.
C++ RAII eliminates most of the need for local gotos though - you can make local classes with destructors for cleanup, with the added benefit that it's exception-safe.
But I wouldn't want throw exceptions in a linux kernel function. It introduces too many things going on behind the curtains that may not be safe in a particular situation.
You do realize there are CPUs with user stack pointer(s), right?
You do realize there are cases where the recursion is limited in depth based on the nature of the problem, rather than the size of the data, right?
You do realize there are languages where the amount of data involved in the levels of the recursion can be precisely controlled, right?
It's not as black and white as "recursion bad, mkay?"
I've fallen off your lawn, and I can't get up.
To understand the revulsion some hold toward GOTO, you have to mentally turn back the clock to a time when it was used for almost everything. Back in the wild west days of computering, there were no conventions for organizing program code. There was no Structured Programming. Early languages provided simple branching tools (like IF-GOTO) but no guidance. A good programmer would soon figure out his own way of organizing his code, and he could become quite productive. The problem was, everyone had their own individual, eccentric methods, and looking at somebody else's code was often confusing. Then structured programming came along, and it provided (or some might say imposed at sword point) a common organizational methodology and a common vocabulary. Two programmers who were trained in the doctrine of structured programming could read one another's code much more easily.
If you see the keywords and indentation of a WHILE-REPEAT loop, or a REPEAT-UNTIL loop, or an IF-THEN-ELSE condition, then you already have a clue, you already have a starting point to understand what the code is doing. If you see GOTO, then it communicates almost nothing. Then you have to look at the context. There may also be some code comments. It may not be a problem, and in today's environments there's no reason why it should be. This isn't the wild west anymore, and we don't use GOTO for everything. If it's there, somebody presumably had some reason for it.
While a JMP is equivalent to a goto, keep in mind that conditional loops are a higher-level language construct than what is available to those early processors, which kind of means that JMP/goto had to be used everywhere on those systems. In my personal experience, having written spaghetti code with goto, and also having written vastly less tangled code without it, I'm quite aware of the software-maintenance benefits of avoiding goto whenever it is easy to avoid. But I'm also aware of situations, usually involving getting out from the inside of many nestings of conditions and loops, where goto is clearly the simplest and most elegant way to do that, and I will unhesitatingly use it for that purpose. Those situations are uncommon enough that its use need never be called "excessive".
Do you understand that you are doing more work at runtime to solve an aversion to a source code construct?
You understand how fucking dumb that is?
"His name was James Damore."
Most languages that handle recursion well just translate most simple recursions into iterative loops during compilation anyway.
"His name was James Damore."
Same for MATLAB. In fact, the MATLAB documentation indicates that eval should be avoided.
It's not inefficient if the compiler optimizes it away.
In what way does it beg the question?
Recursion isn't what allocates the stack frames. Limited compiler translate the recrsive algorithm into code that allocates the stack frames. I'm not sure, but I suspect that there is plenty of room for compiler optimization compared to what they are doing now, but I am not certain that they are still doing as you say they are.
If you are using gcc, you can also use the "cleanup" attribute to achieve something similar. I have a feeling that one day this will become part of the C standard.
Avantgarde Hebrew science fiction
And what are you doing with that stack if still not recursion. You don't just magically make it 'not recursion' by implementing one element that you didn't even change the name of differently
There are much more errors, like fopen(f) :)
Cost free eBook I read (by iBook/Kobo/Amazon/ObookO/Gutenberg etc.): "The Green Odyssey" by Philip Jose Farmer.
(And I'm not saying 'goto' is a bad thing. Using it to uncreatively break out of multiple nested loops or do error handling is easier to understand than the alternative. Also, in about every programming language, there are pretty much always several ways to achieve a certain behavior. The one that is easiest to understand should be chosen unless there are pressing reasons for one of the other ways.)
Disregard my rant about maintainability if you code one-shot things that no one - including you - will look at again once you're done.
Internal company politics, probably.
I don't get why people defend the usage of GOTOs with claws and teeth.
In modern languages, and that includes C, the question if to use or not to use a GOTO never was an issue!
The phrase 'GOTO considered harmful' comes from times where the predominate language was FORTRAN!!!
In FORTRAN you easily see a 1000 lines program with 1 or 2 dozens of GOTOs, where the same program written in proper C or Pascal (and any other modern language) has zero.
Furthermore, the example of 'good use of goto' given in the linked article is bollocks. I would fire (or educate) a programmer giving me code like that.
Cost free eBook I read (by iBook/Kobo/Amazon/ObookO/Gutenberg etc.): "The Green Odyssey" by Philip Jose Farmer.
Using your own stack doesn't make it 'not recursion'.
But the entire modern GUI API is based on "event driven" programming. Replete with "OnRightButtonDown()" , "OnWindowClose()" ... . These are nothing but COMEFROM statements. COMEFROM could be as harmful or even more harmful than GOTO. With a good design based on a valid state machines and object oriented code we not only handle these with east, we are successfully developing incredibly complex code.
So, no. We did not forget GOTO just because some authority figure railed against it. We replaced it with a better concepts like event loop, event dispatching, object orientation.
sed -e 's/Chuck Norris/Rajnikant/g' joke > fact
And, after this, we'll see and article titled "Developer argues for unilateral editor switch" that ends in "it begs the inevitable question. Are you ditching emacs in favor of vi -- and should you be?"
I was there.
Circa 1980, GOTOs in early BASIC and also 6502 Assembly were appropriately used to maximize the limited resources of early desktop computers. A particularly elegant technique on the Apple II was to POKE instruction codes into the keyboard buffer and GOTO it (the Lamb technique IIRC). While the KB buffer was only something like 128 bytes, it was long enough that a GOTO to a computed destination could be built in it and, wowsa, suddenly Applesoft BASIC had a very powerful CASE emulation.
Naked GOTOs were no longer needed when disk drives replaced tape drives, and RAM grew from 4, 8, or 16 kilobytes to the incredible size of 640 kilobytes. We still used GOTOs that were clothed within Structured Programming constructs (IF-THEN, DO-UNTIL, WHILE-DO, etc) but those were tamed GOTOs. The wild, naked GOTOs became much more rare and good programmers charged with maintaining legacy software would savagely hunt them down and destroy them.
Meanwhile, Gee-Whiz BASIC (arguably the only really good thing to ever come out of Microsoft) let us replace line numbers with labels and brought about the Business BASIC revolution circa 1985.
Dijkstra first used the phrase "GOTO considered harmful" in 1968, only 3 years after BASIC was written and about 7 years before BASIC was widely used (the costs associated with moving from Big Iron using centralized card and tape readers to minicomputers with networks of remote terminals slowed BASIC's adoption.) He was talking about FORTRAN and COBOL practices. His work was part of the slowly dawning recognition that it was not sufficient to write a program that solved the problem; that you also had to write it in such a way that you could maintain it or repurpose it next month or next year. That was the dawning of what became known as structured programming practice.
Bringing this back to the present, using recursion makes a great deal of sense when time to production, long term costs of code maintenance, or repurposing are things that need to be considered.
Obviously if the code is one-off throw-away, like a tool that will be used in converting the accounting system database from warehouse inventory to just in time purchasing, then maintenance is not a consideration but neither is efficiency. Slap together whatever will work and get on to something else asap; don't take time to rework a recursion into something faster or more robust unless the software breaks on a pre-production trial run. And then look for a quick and dirty fix.
But if the code is likely to still be in use five years in the future, then write it so the poor bastard whose got to maintain it can understand it as quickly as possible. That could well mean using recursion. The same goes if chunks of the code might be re-used in some other way, say for example taking chunks from an inventory application to build a library system for maintenance manuals.
Also keep in mind that today's hardware limitations will not apply to tomorrow's problems. It is perfectly acceptable to use a recursion that you know will fail on the 20th iteration if you also are assured that there will never be a need for more than 19 iterations in the next 5 years. In other words, don't waste yourself trying to fix tomorrow's problem, which may no longer be a problem when tomorrow rolls around.
Goto is for quiche eaters. Real men use setjmp/longjmp.
goto is for people who can't understand nested code.
In this example, you get much cleaner code from inverting your if conditions so that they're checking for success, not failure, and the proceed version is inside the next indentation level. You can then cleanly see what failure condition corresponds to which cleanup, in both directions, using the indentation level. Linux developers are allergic to this for two reasons. The first is that they set their tab width to 8 and have an 80-column maximum line width, so code written with this kind of structure quickly runs out of horizontal space. The second is that GCC 1.x generated much better code from the goto version (since 3.0, probably 2.x but I've not actually tested it) it will generate byte-for-byte identical code from both.
I am TheRaven on Soylent News
While you're free to want your OS written in assembler, I prefer mine to be written in compiler.
CLI paste? paste.pr0.tips!
It's only the first thing you learn to do in SICP.
Not even MIT still uses SICP, sadly.
Socialism: a lie told by totalitarians and believed by fools.
Clang and GCC won't even warn when you do this, but if b is true then this code contains undefined behaviour (the initialisation of y is not reached - even though the initialisation is inline with the declaration). In contrast, the standard explicitly prohibits jumping over a declaration in other contexts. If you restructure the above example to use a switch statement to jump over the declaration of y, then you'll see the code rejected at compile time.
I am TheRaven on Soylent News
regularly. About every other month. It's kind of eerie how reglarly it happens that I'm saying 'this is an A and a B with some new stuff but I can't do this can I" to my java IDE.
Java added back multiple inheritance via "default implementation of interface methods". It's awkward, but that's java for you.
Socialism: a lie told by totalitarians and believed by fools.
Maybe not better than return, but goto might be better for breaking out of deeply nested code.
I've been coding in C professionally for a quarter of a century now. I still haven't run in to a situation where I thought, "Wow, that guy's use of goto makes things so much cleaner." Run in to plenty where I thought, "WTF is going on here?"
Goto breaks the pattern for control flow through a program. When you need to debug the program, goto makes it needlessly hard to figure out how you reached that piece of code.
And frankly, if you can't write a clean, efficient program without goto, you're not very good at programming.
Moderating "-1, Disagree" is simple censorship. Have the guts to post your opinion.
"It's sometimes better to light up a flame thrower than to silently curse in the dark"
> Without recursion, data structures like trees or graphs are useless. Kind of a problem if you have a directory tree structure, or you want to follows URL links for web scraping.
I would say web scraping is a good example of when you should NOT use a recursive function. My first web scraper looked like this:
scrape_page(url) {
links = get_links(url);
foreach(link in links) {
scrape_page(link);
}
}
On a site like Slashdot, which has many pages (on for eaxh comment posted), that fails wonderfully.
My i improved version, which has been running successfully for many years, looks like this:
scrape_page(url) {
links = get_links(url);
foreach(link in links) {
add_to_queue_database(link);
}
}
Note scrape_page no longer calls itself, it's not a recursive function. It's driven by this main() function:
while (url = get_url_from_queue_database() ) {
scrape_page(url);
}
The queue is held in a database which can easily store hundreds of thousands or millions of urls, There is no recursive function and it doesn't die when spidering a forum. It also doesn't lose all its queue when stopped and restarted.
Some people would point out this solves a recursive and say it's therefore a recursive algorithm. The algorithm isn't recursive by definition because no function calls itself, even indirectly.
Good programmers may not be commonplace and that in itself might be to reason to ban freedom and make everything as constrained and idiot proof as possible... I'm not one of those people... I am for formal language "modes" where you can specify your skill level. Then GOTOs can be forbidden except for those who have an approved skill level.
As we should all know, loop and if constructs are merely specialized use cases for GOTO. Not every situation has been abstracted into a safe language construct. C lacks the ability to break out of nested loops, same with C++. One has to work around the language constraints adding complexity and performance loss. Now adding an argument maybe a label could have provided another abstraction for another GOTO use case for specifying the loop to jump to.
Exception handling is another abstraction use case for GOTO. Along with the "come from" event model we use which is especially awkward in async and parallel situations. These continue to have poor abstractions that add complication trying to do things that a GOTO or an evolved GOTO would address. I'm saying we should leave power tools around for those who know how to use them so that better abstractions can be created for the lesser programmers. Exceptions being a great example which really couldn't be done without an evolved GOTO but behind the scenes language programmers did all the difficult work. For async event callback coding we currently have kind of a mess everywhere with Java 8 being an impressive hack but only to bring it into the same mess.
How about call back closure situations where you flip the situation and have the code block until the async completes but it jumps to a function or up the call stack for the idle running code. Quite often you have the a where you just want to spawn async tasks and the response to the async task belongs right next to it (hence the popularity of closures.) So now you don't have closures, it just blocks and life is easier "the fork in execution" instead is jumping to the idle code instead of jumping to the callback code. Do I make myself clear? The OS does this already for processes-- your process blocks without any concerns and the OS shifts processing elsewhere.
Democracy Now! - uncensored, anti-establishment news
Developers who speak out against GOTO, show their amateur or assumed armature level or experience. `I had a prof in University who spoke out strongly against it, even going to lengths such as costly loops and costly checks to get around using a simple GOTO, his code was made worse because he refused to use it. If you honestly feel GOTO is an unexceptionable statement in programming, then please write a program in ASM and NEVER use JMP!
History lesson.
https://en.wikipedia.org/wiki/Considered_harmful
Future history lesson: Mostly Harmless
It must have been something you assimilated. . . .
Even without the corner cases related to declarations, being able to enter a block anywhere other than the start makes reasoning about control flow harder than it needs to be.
I am TheRaven on Soylent News
That's a bit subjective. I think I would call it an alternative pattern.
I have seen it work well but more often than not I have seen create code that looks like an arrow. In real life, outside of trivial snippets, arrow code can be horrible to read and maintain. That's not always the case though hence I don't say it's a "worse" pattern - just an alternative.
Its not really cleaner though if you are doing a lot of operations that check for errors. You could be doing a bunch of file io, for example, and need to check for errors after pretty much every line.
Nesting 50 levels deep for success checking is not 'cleaner' and it obscures any actual conditional logic and structuring you've got.
Memory cleanup is a nonissue in many languages. A lot use GC to dispose of memory, and C++ uses destructors. Putting cleanup code at the end of a function is somewhat fragile, particularly in a language with exceptions.
"When you have eliminated the unacceptable, whatever is left, however improbable, must be the truthiness" - Holmes
There are situations where multiple if(a) free(a); results in cleaner and more compact source code.
Contribute to civilization: ari.aynrand.org/donate
You might want to free your memory and close your open file on success also. Now, if you were using C++ and unique_ptr, everything would automatically be freed and you wouldn't have a memory leak if you add an allocation and don't put the free() in at the end.
"When you have eliminated the unacceptable, whatever is left, however improbable, must be the truthiness" - Holmes
Nested code is for people who can't understand goto.
Contribute to civilization: ari.aynrand.org/donate
In C and C++, "break" usually replaces "goto" for leaving loops, but when you need to break out of multiple loops there's really nothing better than a goto with a nice descriptive target label.
"When you have eliminated the unacceptable, whatever is left, however improbable, must be the truthiness" - Holmes
One of the reasons I use higher-level languages is that abstracting away the details works a lot better than paying attention to them. If I use a break; in a for loop, I know at a glance what it's doing. If I substitute a "goto", I have to pay attention to the target line. Switches work more like if...else constructs than gotos, unless you're using something like Duff's device.
"When you have eliminated the unacceptable, whatever is left, however improbable, must be the truthiness" - Holmes
Sigh.
CLI paste? paste.pr0.tips!
That's twisted. My own instinctive way to do it would be the first ..., then if (!exit_cond) ....There's no reason to use a "goto" here.
"When you have eliminated the unacceptable, whatever is left, however improbable, must be the truthiness" - Holmes
I designed an audio DSP processor that had to always run exactly 1024 instructions and then start again at instruction number 0. There was no jump instruction. Instead, when conditional operation was required, the next N instructions could be prohibited from writing or setting condition codes.
Contribute to civilization: ari.aynrand.org/donate
You're missing the point of language abstractions. Ideally, we write our program in an easy-to-understand manner, and the compiler makes it run efficiently. In practice, this doesn't work as well as we'd like, but we're specifically talking here about a compiler that does what we really want.
"When you have eliminated the unacceptable, whatever is left, however improbable, must be the truthiness" - Holmes
Maybe if you use some new hipster frameworks. Real programmers know their constructs and when to use them.
By your criteria, Python is the wrong language, and this is intentional. See Guido van Rossum's explanation: part 1 and part 2.
Eval is not something to be used casually, but it's vital to how some languages work. My general rule is that, if you use it while writing application code, it's probably a mistake, but it can be very useful in libraries to do things and the like. Much like C++ template metaprogramming: very powerful, and most people should leave it for someone else to write.
"When you have eliminated the unacceptable, whatever is left, however improbable, must be the truthiness" - Holmes
Please do not do this.
Do one allocation of the sum of the 3, taking alignment into account (ie: 8) and divide the allocated block to the relevant objects.
One NULL test.
One free.
I understand you were making a point, but we are speaking about "good ways" and this itches so much I had to reply -_-;;;
Irrelevant news and morons using moderation to mod down what they disagree on. 2018 resolution: so long.
No, I prefer things to be simple and portable.
Besides, what you're doing is premature optimization, the well-known root of all evil in programming.
CLI paste? paste.pr0.tips!
Carry flag. Sign flag. Overflow flag. Zero flag. Fucking flag.
if(fucking) goto fubar;
Contribute to civilization: ari.aynrand.org/donate
Translation: Its best at least once! Therefore its always best!
Both your lack of knowledge and lack of logic skills do not surprise me.
"His name was James Damore."
You're missing the point of language abstractions.
You mean like the "low level" C abstract machine?
There is a reason the abstraction is similar to how hardware handles memory.
Conclusion: We do not choose our abstractions based on human needs alone.
"His name was James Damore."
taking alignment into account (ie: 8)
Full disclosure... I wrote the bug ridden sloppy pseudocode to make a point. Fisted cleaned up pretty nicely into something that essentially did what i was showing in a cleaner way.
I kind of wish he'd preserved the multiple goto labels but they weren't needed in my example, although there are cases where they would be... where you want specific error handling for certain errors. Here is some hopefully less awful pseudocode.
{
/*allocate resources */
if [allocate resources1] fails goto: errorhandler1
if [allocate resources2] fails goto: errorhandler2
/*do something */
code to do something useful with the resources
result = 0
goto:cleanup
/* error handling */
errorhandling1:
do something in response to 1
result = -1
goto: cleanup
errorhandling 2:
do something in response to 2
result = -2
goto: cleanup
/*cleanup*/
cleanup:
if resource1 is allocated free it
if resource2 is allocated free it
return result;
}
Okay, but I still think this points to a deficiency in the language with regard to error handling constructs, rather than being an argument in favour of goto as a language construct (other than as a stop-gap measure).
I'd say that's still recursive however with a buffer.
Map the data flow and you should see why I think it's still recursive...
Some people would point out this solves a recursive and say it's therefore a recursive algorithm. The algorithm isn't recursive by definition because no function calls itself, even indirectly.
The algorithm is still recursive; the recursion is just built in to the language in the form of predefined iteration constructs like "foreach" and "while". Any time you have an iterative program of the form "first do X, and then repeat the process for the remaining input", that's recursion: one of the steps in the process (the repetition) is defined in terms of the process itself.
Some recursive algorithms can run in constant space, while others (such as most operations on trees) inherently require more space to process larger inputs—even when implemented "iteratively". The space used does not depend on whether the recursion is implemented with explicit function calls or loops, but rather on the quality of the compiler, including, but not limited to, tail-call optimization. Far from being complex wizardry, this optimization is really just the correction of a common implementation flaw wherein a function's stack frame remains allocated until the function returns, rather than being freed once it is no longer needed. This simplifies the compiler slightly at the cost of forcing the programmer to distinguish between implicit and explicit recursion, thus impairing the readability of naturally recursive algorithms and sparking endless debates over what ought to be a trivial difference of coding style with no runtime effect.
"The state is that great fiction by which everyone tries to live at the expense of everyone else." - Bastiat
It may be very well advisable for Matlab's eval, especially since Matlab itself is better avoided. In other languages, you can often use COMPILE instead of EVAL anyway. (Chances are that this is what you actually want to do in many cases.)
Ezekiel 23:20
Because non-hardware stacks on your computer use some special kind of physical or virtual memory that is somehow magically more plentiful than the memory your computer uses for hardware stacks?
Ezekiel 23:20
Technically, MIT does still use SICP. I believe the class is called "Advanced Symbolic Systems" or something like that.
Ezekiel 23:20
Arguments might be avoided by saying it's not a recursive FUNCTION - no function calls itself, can't run out of stack space. It *is* a lot like what happens when you start with a tail-call recursion then remove(?) the recursion. If somebody wants to call it a recursive algorithm I feel no need to really argue about it, though I will ask you a question or two.
> Any time you have an iterative program of the form "first do X, and then repeat the process for the remaining input", that's recursion: one of the steps in the process (the repetition) is defined in terms of the process itself.
Care to point to which step "is defined in terms of the process itself"? I don't see that anywhere.
It's very similar to recursion, but my understanding of the definition of recursion is that the definition/implementation of X must reference X. I don't see that in my code anywhere.
What's your definition of recursion? My informal definition:
X is recursive if and only if X is defined / implemented by calling X, directly or indirectly.
One dictionary definition is similar:
A routine, function etc in which a part is defined in terms of the whole.
Just because the output of a function X(a) may be, at some later time, be passed to another seperate instance of X(b) doesn't make it recursive, imho, if X(a) TERMINATES before X(b) begins, because X() makes no reference to itself.
I kind of wish he'd preserved the multiple goto labels
But eliminating those was the whole point of the cleanup. Having to maintain (and get right) the per-failure jump targets is, IMO, more of a mess than just doing the cleanup right away without jumping.
And if it's just about returning a failure-specific exit code, you could implement that without multiple labels by assigning to a variable before jumping.
CLI paste? paste.pr0.tips!
Here' s an alternative definition:
A recursive algorithm is defined as any algorithm which is recursive. ;)
Goto does ease redability of complex logical jumps .. and would still be a prefer choice.
Recursion bit difficult(or impossible) to replace -- it may change in syntax, and may be called something else, but at core recursion does exist -- whether in parser, far known leaf node traversal, email parts parsing etc etc....
They both are neat and clean ... I always support them, and use them as and when necessary.
I didn't intend for "exception handling" to mean anything more specific than "some way of dealing with errors". I think the term can be used with this general meaning, although I suppose it is also commonly associated with a specific mechanism. In any case, if the mechanism for dealing with errors in a programming language is broken, I think the best solution would ultimately be to fix it, rather than forever working around the problem.
A chainsaw don't know the difference between a log and a leg. But sometimes it's the right tool.
http://web.archive.org/web/200...
Something like a decade ago, I tried throwing out everything I'd been taught about program optimization and just optimizing for cache use. The speedup was remarkable.
"When you have eliminated the unacceptable, whatever is left, however improbable, must be the truthiness" - Holmes
Elevating anything in software development to the level of a religion (except vim over emacs) is as crazy as any other religion. Similarly, saying that a developer should use a language construct where inappropriate because it might be appropriate in another application is crazy. Obviously, you're going to use jumps in assembly code, but you need to examine them to make sure they're doing what they should. In C, most applications of goto should be replaced by appropriate control structures that make it obvious what they're doing and reduce the chance of mistakes.
"When you have eliminated the unacceptable, whatever is left, however improbable, must be the truthiness" - Holmes
If a is a pointer, if(a) tests a against the null pointer. This is unnecessary because free(NULL) works. It's also deceptive since a non-null pointer doesn't have to point to anything useful, and free(a) doesn't set a to the null pointer. I've seen heavy use of macros in C, where the macro executes free(a);a = NULL;.
"When you have eliminated the unacceptable, whatever is left, however improbable, must be the truthiness" - Holmes
The 1989 ANSI standard didn't remove any functionality, but made the language cleaner. If it would be awkward in C89, it would be awkward in K&R C.
"When you have eliminated the unacceptable, whatever is left, however improbable, must be the truthiness" - Holmes
What most people miss on Y2K is that given the constraints at the time most of those programs were written, the cost of avoiding the problem would have been roughly triple (in real terms, discounted dollars, etc.) as ultimately fixing it was.
Yes, economists went back and actually measured, estimated, etc. Also, most of the code from the era did not, in fact, make it to Y2K, and would have incurred the costs without receiving a benefit.
Although the resources would be negligible today, we are talking about the extra labor to punch two more digits on each and every 72 column card, the limited number of digits on those punch cards, memory for which it was a break thought when the *rental* price dropped down to a buck a byte a month, the extra processing time to handle three or four digits, etc.
It also didn't help that so many (a strong majority?) coded in the year ending in 00 exception to leap years without including the divisible by 400 exception to the exception . . . other attempts to accommodate 2000 might have had similar blunders.
hawk
Dijkstra first used the phrase "GOTO considered harmful" in 1968
Not even then, actually. The title of the letter, as published in CACM, is "Go To Statement Considered Harmful"; and that title was added by the editor. I just skimmed through it again, and unless I missed it, he never even uses the word "harmful".
He does say that "go to" (boldface in the original) is "too primitive", and refers to its "undesirability" and "disastrous effects" and "(logical) superfluousness".[1]
But the passive construction "considered harmful" is, just as its most natural interpretation would have it, an indication that someone other than the writer considers it harmful. That someone was Dijkstra; and while he cites others in support of his position, it would have been rather unlike him to step back and crouch behind the passive voice. The man was a first-order curmudgeon, bless 'im.
[1] Prolepsis: Dijkstra's "(logical)" here is meant to indicate that yes, he is perfectly aware that Von Neumann machines implement all branches with some form of "go to". That it must be available to the machine, and thus will typically be available in assembly language, does not mean it has to appear as an arbitrary branch construct in higher-level languages.
It is rarely "the right thing," typically because it is super inefficient.
In compiling implementations of programming languages, it's anything but inefficient.
More and more platforms have begun to establish Data Execution Prevention (W^X) policies that give third-party applications no way to flip a page from writable to executable. These include iOS, Windows Runtime, and current-generation game consoles and gaming handhelds. Thus all compilation to native code must be either done ahead of time before packaging or done by an operating system component, such as WebKit JavaScript in iOS.
(Exclude recursion here - i think he talks about the examples given in school for recursion, which often are not good in terms of resource usage)
About the rest: Bullshit. These are not forgotten, and they are forbidden in most coding guidelines for a reason. His musing about that goto is only bad if you jump "backwards" is borderline funny.
There is a reason that goto should only be used in extremely rare occasions (and i would agree that some of these *may* be for kernel programming). The examples of this "programmer" are excellent examples why for people like him these should be forbidden. Any of his examples are better off without goto, and if you don't see a more efficient, clearer and safer solution without his shit, you should not program any high-level language.
Eval is another piece of cake (I exclude python here because it seems to get eval right). My practical experience is that i is rather overused by inexperienced programmers. Filtering a string that should be evaled in a way that it has a predictable outcome is as difficult as writing an eval. Typically my recommendation would be that evals need to be a) sanctioned by a senior developer (just to make sure nobody uses these out of pure incompetence) b) encapsulated in a function (e.g. for the dection of a laguange feature/level) c) filtered carefully if they accept user input.
GOTO was kinda special...It was to either completely exit a procedure when the input conditions were just TOO F'd up.
Quite often the jump was to the equivalent of a "Halt and Catch Fire" sorta error processing.
GOD, I LOVED recursion. Of course, I slung a boat load of LISP code in my younger days.
Some would say this is bad programming, but if you are programming a pacemaker, heart monitor or the like, an infinite loop is EXACTLY the construct you want. I've written some pretty ugly code (breaking out of deeply nested loops) when a simple GOTO would have cleaned things up a lot. They are all tools that have a place and time to be used.
Care to point to which step "is defined in terms of the process itself"? I don't see that anywhere.
It's part of the compiler's internal definition of the loop construct, and does not appear in the source code. In the object code it can be identified as the opcode that branches back to the beginning of the loop.
This becomes more obvious if you consider programming languages where iteration is not a built-in construct, and loops are written in terms of explicit recursion. In Haskell, for example, a simplified C "while" keyword (disregarding "break", "continue", etc.) could be implemented as the following recursive function:
Or using the fixpoint operator, without referencing the current definition by name:
Building on the fixpoint example above, would you call this a recursive function?
Sometimes it isn't so simple. The body of f() does not refer to f(). However, what happens if you call f(f, 0)? The function might be recursive, or it might not be, depending on the arguments. It all depends on that function pointer.
Arguments might be avoided by saying it's not a recursive FUNCTION - no function calls itself, can't run out of stack space.
It's not a recursive function, since it's not the entire function that's repeated. It would be more accurate to say that it contains a recursive function, though that inner function is hidden behind the loop construct. Barring the use of alloca() or similar it won't run out of space on the function-call stack, since alloca() / variable-length arrays and function calls are the only ways to allocate from the function-call stack, but the iterative version of the function still needs to track the same data and can still run out of memory given large enough inputs—just not stack memory. Sometimes that distinction is important, but more often it isn't, especially given the stack sizes available to programs running on most virtual memory based operating systems, ranging from several megabytes to "malloc() would fail first". (After all, some languages allocate stack frames from the heap.)
Anyway, the fact that (unoptimized) tail calls use stack space while iteration constructs ordinarily do not is nothing but an artifact of poor compiler implementation, and easily corrected through tail-call optimization. It's really no different from the way some languages with simplistic garbage-collection algorithms (e.g. Perl) fail to collect sets of objects with cyclic references. You can either take pains to avoid cyclic references (tail recursion), or just switch to one of the many modern languages which don't cause arbitrary runtime errors when you write code in the most natural style. Given a reasonable guarantee of tail-call optimization, function calls in general may allocate additional function-call stack space, but a call in tail position won't, so tail recursion is exactly the same as iteration in terms of expected stack use.
"The state is that great fiction by which everyone tries to live at the expense of everyone else." - Bastiat
> but the iterative version of the function still needs to track the same data
It seems to me that it does not. Looking at the actual state information of my program after running for three years appears to confirm that.
In a moment, you may wish to refer to my code posted above, but we'll start by talking about the clearly recursive version.
Some years ago, I pointed my spider at a large, deep directory of urls with many links. Imagine dmoz.org. Since then, it's followed links several million deep. Were it recursive (in the traditional sense), there would now be several million copies of the function *running*, each with a stack pointer to its parent. For the URL it's currently spidering, we'd have the full history of how it got there, the url that linked to it, all on the stack. Also any other variables local to the function would still be live in memory - millions of instances because millions of instances of the function are still running.
Using the iterative algorithm I posted above, I think you can see that we do NOT track any data whatsoever about how we got to the current URL. All of the information about past pages, which would be on the stack in a recursive algorithm, do not exist in my program. So in fact it does *not* "track the same data" and your idea that it "can still run out of memory" is disproven by the fact that it's been running for years, millions and millions of iterations, and it's still using the same 3MB of RAM that it started with, plus a few MB for the queue.
You are certainly correct that you *can* simulate iteration by using recursion. That doesn't mean you must.
The key point, to me, is that at least what I mean by "recursive" is that there is a parent and a child. The parent doesn't return/exit until its child does - and its child, and its child, and its child, and its child ...
With the iterative algorithm, one completes, and releases all of its memory, then another one starts. There is only one instance running at any given time, and therefore only one copy of the variables. There is no "parent", only "previous", which has no other relation to the currently running code - the previous URL isn't even the one that linked to the current url!
Using the iterative algorithm I posted above, I think you can see that we do NOT track any data whatsoever about how we got to the current URL. All of the information about past pages, which would be on the stack in a recursive algorithm, do not exist in my program. So in fact it does *not* "track the same data"...
You're comparing apples and oranges. The iterative version of your program is not using the same algorithm as the recursive version. You replaced a depth-first search with a breadth-first search, which makes your example completely irrelevant for comparing iterative vs. recursive implementations of the same algorithm. Among other things, the order in which your web crawler visits pages is very different in the iterative version. To maintain the original order you would need to restore the depth-first search algorithm, which would imply tracking the chain of pages you came from—exactly like the recursive implementation, though you could store that information on the heap or in a database rather than on the stack. (Note that a tail-recursive implementation would also store that information on the heap or in a database... this isn't a matter of recursion vs. iteration.)
You are certainly correct that you *can* simulate iteration by using recursion.
The tail-recursive implementation is not "simulating" iteration. It is iteration. Expressing the same algorithm with a loop construct rather than an explicit function call would not make it any less recursive; the loop implies a built-in fixpoint operator. If this trivial difference of style makes any difference in how much stack space your function uses, that's a bug in your toolchain.
The key point, to me, is that at least what I mean by "recursive" is that there is a parent and a child. The parent doesn't return/exit until its child does - and its child, and its child, and its child, and its child ...
That is not what "recursion" means. Moreover, how would that definition even account for something like tail-recursion, where the "parent" is replaced by the "child"? Logically, the "parent" still does not exit until the "child" does, but in the meantime it occupies no space on the stack and the return from the innermost "child" is also the return from all of the "parents". No matter how deep the tail-calls become, the stack use remains constant. Recursion only requires additional stack space when there is more for the function to do after the call, i.e. when it is not tail-recursion. Such cases cannot be converted to iteration (using the same algorithm) without shifting all the data required by the continuation from the function-call stack into some other data structure.
"The state is that great fiction by which everyone tries to live at the expense of everyone else." - Bastiat
When we started this discussion, that quote at the bottom of the Slashdot page was James Baldwin
--
Those who say it can't be done are usually interrupted by others doing it.
--
> The iterative version of your program is not using the same algorithm as the recursive version.
Okay so you acknowledge there are iterative algorithms, there are recursive algorithms, and not all iterative algorithms are recursive, right? That's an important point.
> You replaced a depth-first search with a breadth-first search
In fact it's (mostly) depth-first.
> the depth-first search algorithm would imply tracking the chain of pages you came from
Let's try a little experiment. Clear your browser history, then try clicking a link. Amazingly, you can go forward (in depth) without knowing your full history of where you came from! This isn't theoretical, as I said, I've spidered millions and millions of pages, by roughly following the first unvisited link on each page. If I were in the mood, I'd give you a core dump from my spider and you'd notice that nowhere in the core does it have the starting page from several years ago. (Unlike a recursive tree walk, which would have that data.) "But how do you get back, to follow another branch?", you may ask.
The first thing apparent is that once you've spidered a page, you're not going to spider the same page again, so you can forget all about it! There is no need to track all the parents. You need only remember some *unvisited* pages, none of the visited pages. So no, it's not correct to say "depth-first search algorithm would imply tracking the chain of pages you came from". Rather, you must track the head of the branch you did NOT come from. That'll be where you go next if you end up on http://hmpg.net/ (check out that page).
Further, we're discussing spidering the web, which is first a network, not tree. You seem to be thinking in terms of a tree. More importantly, the web is a real practical, thing, for which I gave an algorithm that's actually been at work for many years, as opposed to walking some theoretical tree. Because of the nature of the web, as a real network, you can discard 99.999% of the information which you would need for a complete walk of a theoretical tree. No need to remember *every* link you didn't visit, you'll never spider every page on the web anyway, and if you wanted to try you could always start back at dmoz.org or *any* large list of links. The network (the actual www) is such that there are many nodes (sites) which connect indirectly to ~every other available site. So the actual working algorithm doesn't know where it came from. What it *does* remember is a list of 1,0000 URLs it can go TO next. That's all it needs to know - where to go next, not where it came from. It ends up being depth-first because I made the list of "next URLs" a 1,000 element LIFO. I've also run it breadth-first by simply working the other end of the list, FIFO.
MySQL will not allow table names to be passed in as parameters to a stored procedure [1] [2] [3]. Without eval, there is no way to dynamically use a stored procedure to operate over multiple, different, tables without enumerating all the tables. The code needed for the "safe" approach is massively bigger, n * m, where m is the body of your stored procedure and n is the set of tables you want to operate over. It also requires such a massive degree of code duplication to make it significantly less safe.
We have a reporting system called MyDBR that requires everything to be done in MySQL or JavaScript. Likely, a stored procedure isn't the best answer for my problems, but it's better than JavaScript. Probably the "safest" answer is to throw away MyDBR and use an actual programming environment to accomplish what I'd like (sane reporting.)
The entire premise of this article seems odd to me. Who would forget these techniques, unless they never really learned them in the first place? And why would the author want to revisit them? I think because it was a slow news day.