AP Test's Recursion Examples: An Exercise In Awkwardness
theodp writes "Yet another example of how AP exams are loaded with poor coding practices," quipped Alfred Thompson, referring to a recursive code example that prints the numbers 0 to 6, which was posted to the (closed) AP Computer Science Facebook group. "We are often forced to use code examples that are not ideal coding practice," Thompson notes. "We do that to make things clear and to demonstrate specific concepts in a sort of isolation that we might not normally use. We seem to do that a lot with recursion because the examples that require recursion tend to be fairly complex." So, while asking students to use recursion instead of a loop to print '0123456' serves the purpose of teaching recursion, Thompson opines that it's also a poor example of code practice. "Someone raised on functional programming where recursion is a pretty standard way of doing looping might disagree of course," he adds. "There is a saying that when all you have is a hammer all your problems look like nails. This seems, in a way, to be the case with recursion and loops. If your first tool of choice (or what you have learned first) for iteration is loops you tend not to think of recursion as a solution. Similarly if you start with recursion (as is common with functional programming) you are a lot more likely to look at recursion as a tool for iteration." So, do you tend to embrace or eschew recursion in your programming?
A legitimate use of recursion would probably be something a lot more complicated, and a lot harder to grasp, than a contrived use that teaches the concept in isolation. It's like teaching a kid long division using 6000 / 10, and disparaging the example saying, "yeah, but you would never use long division for that." Well, no shit, Sherlock. You're teaching mechanics. Application is for another lesson. Maybe even another class.
if you can solve a small part of the problem, given that the rest has already been solved the same way.
I don't think I've ever user recursion in my professional career. Sure, if I ever need to code a BNF grammar I might, but it's just never come up in the real world.
I recommend avoiding it where possible. The average programmer* has long forgotten what little they ever learned about recursion and don't encounter it in their daily work. Using it is, therefore, guaranteed to cause problems for them when attempting to maintain code that contains it. Beyond that, the ones who are clever enough to vaguely remember something about how to use recursion will often either use it where better methods are possible or forget to put in safeguards to prevent overflowing the stack.
* Sturgeon's Law comes distinctly to mind here.
Reading and modifying other people's awkward code.
Horrifyingly, math tests still ask students to do long division when in the real world it's quicker and more reliable to use a calculator. There's this thing about questions on tests, they aren't there to teach you. They are there to test what you know.
Quicksort, the standard sorting metod is normally implemented using recursion. Similarly algorithms that traverse tree and related structures are often implemented recursively.
Has this idiot ever looked at an AP Calc exam? There is no time in my professional career when someone handed me a sheet of paper and told me to integrate e^x^2 from -inf to +inf. There is no time in my professional career when I ever needed to do that integral.
Yet there it was on my AP Calc exam.
Does this mean the AP exam was teaching me "bad math practice?"
No, of course not. The AP exam wasn't trying to teach me a goddam thing, it was trying to test my understanding of the tools at large.
TL;DR: Blogger is an ignoramus, thanks so much for bringing it to everyone's attention.
For most stack machines recursion is bad because the stack cannot grow without bounds and you have no idea where that boundary is. That's the practical problem with recursion.
If something is super fast, the overhead from all the subroutine calls (specifically, the stack manipulation) will make it shit compared to a loop. I think the idea that everything is magical happenings (like idealized math) until late in CS is a definite issue.
Still, for a simple recursive demonstration, that's fine. It would be better to then show which should be looped and which should be recursive. But the fact that you can loop billions of times but I doubt you could recursively call a billion deep should be kept in mind- this is a fundamental limit of function calls.
A lot of the data I work with is tree-like or more generally directed graphs and recusive techniques are absolutely essential for handling them. Often the recusive function contains iteration, ie:
def do_something(node):
for child in node.children():
do_something(child)
Recursive .vs. iteration isn't a style choice, it's a matter of selecting the right tool for the job from the constructs the language du-jour offers.
Public Service Announcement comes on the television. Pages of code scroll by on a computer screen in the background. A strung out looking programmer stares into space, bleary eyed, obviously stressed, pulling his hair out. A voiceover begins...
Using recursion as a simple looping construct in an imperative programming language isn't normal. But, in C++ template metaprogramming, it is.
Template metaprogramming, not even once.
;-)
(Actually, I do a fair bit of template metaprogramming in C++. It can be handy for a certain class of problem.)
Program Intellivision!
You should check preview before posting. The HTML parser removed the "<" from the quoted code, making your example wrong as well.
"Some one raised in functional programming"
Would a) not have combined a recursion with a side-effect, b) we have been sure to make a tail call and c) probably used higher-order functions anyway.
None of which is relevant. This is poor coding style because this is Java which isn't functional and has no tail call optimisation. But, this is a standard problem with assessment; it can be very hard to come up with good and realistic examples, while limited to the knowledge that the students have at the current time. Try and you might, at times, you show less than optimal code, because to write better code you have to teaching something else entirely.
... not a programming test. Recursion is a key concept in CS and is the foundation of many techniques and principles. Sure, no one uses it in practice that often, but that doesn't mean you shouldn't know it if you're learning CS.
If you want to train people for job interviews, send them to a trade program. If you want them to understand the field, you have to teach them the fundamentals.
I never use the central limit theorem directly when doing stats, but knowing how it underlies the methods helps provide a better understanding of results.
-Chris
Recursing a directory/folder structure would be a much better example of not only HOW to use recursion, but when and why it actually makes sense.
TFS asks "do you use recursion?". When it makes sense, such a directory or category tree. That is, when the problem is inherently recursive. It's also good to know that all recursive algorithms can be trivially converted to iterative versions with an explicit stack or queue. This is useful for something that could recourse deeply, like following links to spider the web.
"Smart" programmers sometimes forget that a strong emphasis on readability and maintainability are very important facets of being a great software engineer.
You may be "smart" today to show how clever you are, but the next guy in has to re-learn your "cleverness."
Normal loops risk never ending.
Loops made by recursion risk ruining your stack to boot.
Recursion is mostly a hobby of mathematicians and in practice is a tool that should be used ultimately sparingly.
Religion is what happens when nature strikes and groupthink goes wrong.
The test doesn't actually ask you to write a recursive function. It shows you a function and asks you what it does. _That_ is a basic skill that any real-world programmer must master: Look at some code that might be coded badly, and figure out what it does.
The minor detail that the bad code uses recursion is irrelevant. And just like I don't learn bad practices from bad code that I see, I don't think anyone would learn bad programming style from this example. Anyone who can figure out what the code does will know how to get the same result with simpler code.
It's an exercise in determining the behavior of the function, not instruction for the right way to achieve what the function does. In the real world, you have to be able to follow sub-optimal code and know what it does. Anybody with the skills to follow this function will know one or more better ways to achieve the same result.
--I'm so big, my sig has its own sig.
-- See?
I tend to use recursion if it fits the problem well, and the cost of doing it iteratively would (seem to) be higher than the expected imposition on the stack. This varies with the language and whether the implementation supports tail recursion optimisation.
But I've always felt that certain people's fascination with recursion was about on par with certain (possibly other) people's fascination with, say, hashing's O(1) lookup complexity without regard for the size of the associated constant, that in some implementations would easily allow for quite a large N in structures with O(log N) lookup complexity, possibly to the point of running out of memory first. Or, say, the default use of hashmaps with generic and expensive hashing algorithms in some languages, even for structures with but a few members, so even linear search might be competetive.
On the gripping hand, this guy's complaint says more about his inability to find reasonable examples to illustrate recursion than it says about the existence of such examples. I might mention codingbat.com as a counter-point, for example.
Has there been a correction in the article? The code in the article now prints i, making it correct.
Religion is what happens when nature strikes and groupthink goes wrong.
I don't think I've ever user recursion in my professional career. Sure, if I ever need to code a BNF grammar I might, but it's just never come up in the real world.
Whether you use it or not, recursion plays an increasing role in our daily lives.
If it weren't for recursion, you wouldn't have that computer! Or that chair you're sitting on. Or that coffee...
However, I do use recursion for looping in one situation - if my loop is running non-blocking as part of a larger event loop. Example below (excuse the dots leading underscores, slashdot doesn't let me format python). Normally, you'd just use while True for a loop like this.
Most programming is mainaining somebody else's, usually poor, code.
Do not use recursion if it makes you feel clever, or if you're showing off your mastery of some programming language's syntax. Don't use recursion for efficiency. Writing efficient code rarely matters. By rarely I'm not saying never!
Use recursion if it results in the most understandable and easy to grasp code for someone, probably currently in junior high, 10-15 years from now.
In 15 years processors, IO, and storage will be many times faster and the bottleneck costing your former employer millions in lost profits will be your tightly-coded routines buried deep within the infrastructure.
Not sure about the test, but the summary was certainly awkward.
Iterative implementation.
Unless the particular language of implementation has good support for tail recursion.
I'm neutral. I code both ways, compile with -S and see which one looks better in assembler.
Recursion is a loop.
OutOfMemoryError
... you would use a variation of a map function, for-each function or what ever and use a labda or a closure and _not_ recursion.
Cost free eBook I read (by iBook/Kobo/Amazon/ObookO/Gutenberg etc.): "The Green Odyssey" by Philip Jose Farmer.
Your example of the hammer is apt. However it is easy to distinguish what type of app is right for recursion and what type of app is better suited for a loop algotithm.
If you're calculating the area of an irregular shape using Simpson's rule, you probably want to use a loop to do the calculation. Imbedding your logic doesn't seem appropriate in this case. You are using the loop counter to defind how detailed your calculation should be.
If you're creating, updating, and traversing tree structures, recursion simplifies your logic immensely. Here's a case where recursion is appropriate. You are using the end test in your recursion to determine when you have reached the leaf node in the tree.
These two examples of looping and recursion show that the code used in the AP test question depends on what type of application is involved in the question.
Before looking at recursion examples, students should first learn recursion examples.
Get free satoshi (Bitcoin) and Dogecoins
It's "the right tool for the job" category.
Also, there is an overlap where either iteration or recursion work equally well, and it comes down to preference (clearly the author does not prefer recursion, almost ever, but other people find recursion natural even for iteration. Certainly for mathematicians it is more intuitive than the bizarre syntax of the for() loop that we all had to learn).
"First they came for the slanderers and i said nothing."
In the real world, we don't generally write a computer program that just outputs the message "Hello World". But that's usually the first program when we're learning a new programming language. This simple program teaches us how to create the source code, compile, and run the program. Later we learn how to write more complex programs in that language.
In the same way, we don't use recursion to output a string 0 to 5. But this simple problem teaches the student how recursion works. After the student understands how recursion works, s/he can do more complex and realistic problems using recursion.
In a world where most programmers don't have a clue why they chose a list vs. an array vs. a tree for anything, this is actually an excellent question to test students with.
This is obviously not an optimal solution to the problem it solves, but it is in fact a fairly compact example that can be used to test whether a student understands recursion and can follow code.
The question isn't whether there is bad code on the exam. This was a great question.
The question is, is there also a question relating to excessive or unbound recursion and what will happen if the number is too big.
What about Big-O notation and algorithmic complexity.
I had to implement a compiler and virtual machine last week because I couldn't find anyone else to do it that was available. This is because not enough people understand topics like recursion.
The teacher who posted the article made it very clear that his competence is limited and he himself is quite slow witted by stating the he found recursion hard to understand at first. Why is this even on Slashdot?
Yeah, see the author's comment in reply to someone who pointed out the error.
function recursivePrint(i) {
if (i = 6) {
console.log(i);
i++;
recursivePrint(i);
}
}
recursivePrint(0);
I was in an economics class with a good friend. The class was given an assignment in which they had to calculate some nonsense. The teacher said that any language code or pseudocode would be fine.
The friend and I were the only engineers in the class and apparently the only ones to use recursion to get to the answer. He used head recursion and I used tail recursion. Everyone else apparently solved it with itterative loops.
The TA knew my friend and I knew each other and threatened to report us for cheating. I told her to go ahead and show our code to the prof or even a CS prof. The logic that my friend and I were so different in the code that the fact that we both used recursion was the only similarity.
We hadn't cheated and kinda thought it was funny that it looked like we did.
Now, I wouldn't be surprised if we got our code scanned into some database and a computer said we cheated and we would have no recourse.
Help! I'm a slashdot refugee.
It's a fucking technology, not a moral decision. You use each when it's appropriate.
Recursion is a tool best used when (a) the design is made more clear by its use and (b) you can afford the stack overflow and/or issues in languages without proper tail call support. For instance, tree search can often be coded much more understandably and with fewer errors with a recursive solution. The same with factorial and other functions defined recursively. Others, not so much - want to dick about with an array? Use an iterative construct, preferably a high-level one, like map/foreach/etc.
I've not actually used recursion in a long time, mainly because most language designers are too fucking lazy these days to put proper tail recursion into their language designs, the higher level functional iteration constructs are becoming available in the languages I'm using, and data's becoming too big.
That is all.
I'm pretty sure that no student taking that test would perceive that question as being an example of how to write a program. The AP Computer Science exam takes a perverse delight in double checking that every student can read deliberately confusing code. The posted question is just a mild example. I feel that criticisms of questions of that type should be leveled at exactly what's being tested - reading rather than creating code. I know I personally minded that a large number of such questions on the test when I took the exam were fairly spacial in nature - like predicting the bitmap output of a function.
In the example, the AP test is actually asking the student to describe what the code *does*. It does not ask them to write bad code, only read it. Everyone who develops software must be able to figure out bad code, so we will know what to delete ;)
it's all in the implementation.
That's what I'm saying though, it's not all in the implementation, tail call recursion relies pretty heavily on the code being set up (and kept up) properly so that it is possible.
Even if the compiler will always tail-optimize calls (lots of languages it will try, but may not) you can always change code such that a method/function that was tail call optimized suddenly cannot be, and the way ALL compilers are right now you will not know such a potentially crucial change has been made...
Which will not matter in any test scenarios set up, but as soon as it hits the real world it encounters the thing with a million items to iterate over, or possibly a directory structure unimaginable deep, a tree horribly wide... and the program explodes.
Mind you, I *like* recursion, but it is a risky thing to introduce unless you are really, really sure what will be fed into it and how the recursive function will evolve, forever...
Speaking of forever:
Of course, in someplace like Haskell one can just use the "forever" function which loops some action forever rather than implementing an explicit tail-call loop
Um, is that not sneaking in a normal iterative loop with a conditional check that's always true? What would be the difference from the code that generates and "while (1 == 1)"?
"There is more worth loving than we have strength to love." - Brian Jay Stanley
You never know how deep a directory goes. I never use recursion except when it's tail optimized. Sure, the code for recursive tree traversals looks so much nicer, but they have hidden stack overflow bugs.
Is there ever a time when it's safer to use recursion compared to a loop?
I would use recursion a lot more frequently if compilers supported tail recursion. Not for simple iteration, perhaps, but there are plenty of cases where recursion is a better solution that a loop. For example, recursion is often be appropriate when working through some sort of data structure.
The problem is: The most common languages don't optimize for tail recursion. This means that even shallow recursion will eat memory. If the depth of the recursion is unknown, then the lack of optimization for tail-recursion means that your program may run out of memory - not because of any programming error, but due to a defect in the compiler.
There is nothing inherently difficult about handling tail recursion. Scala does it just fine, running on the JVM. I can only imagine that those responsible for mainstream languages figure no one will use it. Well, we, can't, because your compilers don't support it properly...
Enjoy life! This is not a dress rehearsal.
> So, do you tend to embrace or eschew recursion in your programming?
"To iterate is human, to recurse divine.” -- L. Peter Deutsch
Since the guy who has to debug and extend my code is human (me), I take pity on him and recurse only as needed.
Comment removed based on user account deletion
Nah. I often do "back of the envelope" arithmetic operations in my head in less time than it takes to pull out a phone with a calculator app, fire up a laptop, etc. I find it works well when comparing unit costs in the supermarket.
putting the 'B' in LGBTQ+
Yes, it is a test question. Yes, if you understand recursion, you can answer the question. However, it is poor code, because the recursion is not tail-recursive; anyone who uses recursion with unknown values ('n' in this case) will write a tail-recursive function.
Since it's a totally artificial question, there is no reason that they couldn't have used a tail-recursive function. Lots of students won't know the difference, but crappy code like this is a stumbling block exactly for the students who really do understand what's going on.
Enjoy life! This is not a dress rehearsal.
> You never know how deep a directory goes.
On Windows, no more than 130. MAX_PATH is 260, so if directory names are single characters you can have up to 130 of a\b\c\
On Linux, MAX_PATH is typically 4096, so directories can be 2048 deep.
If you're using 16 bytes per level, you know your program won't use more than 16X2048 = 32KB. A maximum 32KB of RAM usage is acceptable on any system running any of the major operating systems.
It wouldn't hurt to check PATH_MAX just to be sure, or put in your own recursion limit of 2048.
However, I do use recursion for looping in one situation - if my loop is running non-blocking as part of a larger event loop.
I do the same thing in Javascript to asynchronously add autocomplete dropdown entries to an edit box, four at a time on each paint tick:
I'd argue this is one of the best questions you could possibly ask a to-be programmer.
"Here's some contrived, shitty code that some jackass wrote, without comments, before leaving. What on earth does it do?"
In the real world it's best to write for a "lowish" common denominator because future staffing is unknown, but best to know HOW to figure out "clever" code if you encounter it. Thus, the best habits for writing code tend to conflict with learning to read any code you may encounter.
The solution is to point out early that writing code is similar to writing documentation: clarity to other readers matters above parsimony etc. Put that on the test.
Table-ized A.I.
I thought it was the law of programming that recursion be introduced with a Fibonacci sequence generator. I believe this AP thing is breaking the law of programming.
There's a use case where any simple problem that can be solved with recursion should be: template metaprogramming. Have the AP exam authors not heard of compile time constants or do they think that the people taking their exams won't recognize templates? Either way, this should be a non-issue for people in a position to write those exams. The fact that this is even a topic of discussion diminishes the AP exams in my eyes.
Indeed, recursion is just looping with a stack. When you need a stack, recurse. Otherwise, loop. Contrary to TFS, there is clear reasoning behind a competent programmer's choice. The viewpoint of this being a capricious choice made at the "preference" of the programmer suggests that the submitter doesn't understand the vast differences in the internal impelementations of various languages.
No wonder conservatives are upset about Common Core
Table-ized A.I.
Anyone acting as a stickler for syntax should not be making that mistake in a public post that they presumably glanced over more than once.
Surely you mean that anyone acting as a stickler for syntax should not be making that mistake in a public post that _he_ presumably glanced over more than once.
This talked about this as well...
AB HOC POSSUM VIDERE DOMUM TUUM
... MAX_PATH is 260 ...
and
It wouldn't hurt to check PATH_MAX ...
Recursion error?
Someone flopped a steamer in the gene pool.
It's also good to know that all recursive algorithms can be trivially converted to iterative versions with an explicit stack or queue. This is useful for something that could recourse deeply, like following links to spider the web.
Why the FUCK would you ever want to keep an explicit stack? Did your language implementers have brain worms? Stack and Heap are the same, it's just RAM. Function call stacks belong in heap. Each program has its own virtual memory region. So, there's no reason not to increment the stack pointer AWAY from zero (or have it start at max address, and increment down) so that you don't artificially limit the program with stack overflow. Counting upwards, I just trap SEGFAULT and if the instruction pointer was at a function stack allocation, then allocate a page of RAM mapped to the next sequential address required, and resume. There, now your code is free to recurse even in C without stack overflow limited by language implementation.
For fuck's sake people, it's 2015. I'm so fed up with the bullshit: I can make this recursion "iterative" with an explicit stack -- NO YOU FUCKING MORON, THAT IS RECURSION! You're FIGHTING against a MORONIC language implementation by manually implementing FUNCTION calls properly.
wouldn't:
public void printNumbers (int n) { if (n > 0) printNumber(n-1); System.out.print(n); }
Have been better, ie. shorter, fewer loops (or stack frames if no TCO)
Nullius in verba
Recursing a directory structure is not a good application of recursion.
It would, if directory structures were trees. But they aren’t; they may (and usually do) contain symlinks, some of which may form cycles.
For dealing with directory structures in real world, you pretty much have to use an iterative approach coupled with a “have we crawled this yet” set.
You should of course decide how to handle symlinks. In most cases I've come across, a symlink is just a file - not something to be followed. Rsync's default behavior is an example of this - it by defaults copies the symlink, rather than recursing into wherever the symlink points. As I recall, tar is the same.
You need to handle symlinks and perhaps bind mounts based of application requirements.
Windows is only limited to 260 characters in the ANSI API. In the Unicode API you can (should) prefix the path with \\?\, in which case the limit is raised to 32767 characters.
Also, symlinks.
Windows defines MAX_PATH as 0x00000104 I'm sure someone will correct me if I'm remembering incorrectly.
Stack and heap are different because of the use case. A stack has a much simpler memory manager. So you want to use a sstack whenever you can.
Now, if there's a language that lets you think as little as you have to when using the heap and get the performance of using the stack, please do let me know which.
Your ad here. Ask me how!
If you learned assembly, you just roll your eyes.
I mean, this guy really seems not to get that the excercise was not meant to demonstrate how to print 123456, but that printing 123456, as opposed to e.g. 654321 is a tool to demonstrate/show the order of the execution of command in a self-recursive function.
I mean, i have seen bad coding practices in courses (nervous handling of null pointers, and strong binding OOP comes to my mind), but this question has nothing to do with coding, but is purely the shortes way to test is the student understands what happens if the print comes after the call of the self recursive function.
Not quite. The UNICODE APIs and NTFS support up to 32,768. Other APIs are still limited to 260 at least as of Windows 7. Explorer can't handle larger than 260, or at least recently couldn't. So long as the older APIs remain, the maximum safe length is 260. Longer paths may work sometimes, but behavior is undefined in general.
>. Why the FUCK would you ever want to keep an explicit stack? Did your language implementers have brain worms? Stack and Heap are the same, it's just RAM
Granted if you're doing it explicitly you'll more likely use a queue than a stack. But not necessarily. For example, you might process all children in parallel if you're working on a GPU or cluster. The parallelization library will then need an explicit stack.
Note to "it's just RAM" isn't always true. If you're crawling the web, your list of links to be visited will be in a database, not in RAM.
Do not use recursion in cases where iteration is simpler. Use recursion in places where iteration is impossible or much harder. The classical introductory examples of recursion like Fibonacci numbers and evaluation of simple arithmetic terms are perfect for the task. If you use recursion for things better done iteratively, you will only confuse people.
Most ACs are not even worth the keystrokes to insult them. Be generically insulted by this and ignored otherwise.
You left out '0'. It should be '0123456' instead.
Regardless of the final implementation, there are problems where the simplest, clearest solution is a recursive one. You type it in to the computer as fast as you can type, it compiles and runs correctly the first time, and then, if you need to change it, you have a place to start.
On a job interview some years ago I was asked to write C code to reverse a string. I wrote it recursively: interchange the first and last elements, then reverse what's inside.
They liked my creativity. I got the job. :-)
...laura
To the question at the end of the article - I use either depending on language and appropriateness to the problem. Languages like CBM BASIC didn't support functions, so you had to find a way to use loops even if it wasn't optimal.
However, there are lots of non-trivial examples, so why settle for something trivial? It was always a bit of work writing a factorial in older BASICs, recursion is just so much simpler. Or maybe terms of Pascal's triangle? The Fibonacci series perhaps - while there is actually a formula for that, most people won't know it. Lots of possibilities.
I'll give you two problems that yield to recursive approaches very nicely indeed.
The first is pixel-based area fill. The second is the expansion phase of object-oriented PCB trace routing. The third is in the repeated bloom steps of the optimum N-color picker for any >N color space, where N is 16 or more. They're all three very efficient in terms of number of lines, they're quite fast, and they do the job perfectly. You can certainly approach these tasks other ways; but I have yet to see anything as elegant.
These do require an architecture where stack space isn't a problem. Which -- IMHO -- would be any decent architecture out there.
Then comes to mind a remark by an old associate of mine. He would say: "Live recursively... die recursively." I always thought that would make a great t-shirt, if for no other reason than its inherent opacity to the common person. :)
I've fallen off your lawn, and I can't get up.
In event-driven programming, event recursion, in which a handler for an event triggers the same event, can often be a solution that combines the benefits of both looping and recursion: Compared to classical recursion, it doesn't generates an ever-growing memory stack, because the function calls are actually processes sequentially; and compared to classical loop, it provides time-windows for other tasks to be processed in between the sequential calls (pseudo-multitasking), as well as more elegant ways to exit the loop under complex conditions (raising exceptions from within a loop to exit prematurely always looks hackish).
A few times for tree traversals, but not for linear patterns which dont need complications/obfuscation to a simple enough process.
Even when on trees can easily pattern it recursively, but without using a recursive call (finite depth or non-valued descent is usually the case due to other processing self-limitations)
543210
Because System.out.print(n);
It's not telling students to use recursion to output that number sequence, it's asking students to read and understand a bit of code that does it. Being able to read and understand other people's badly written code is absolutely essential in todays software industry...
I think it's because the printNumber(n-1); call does not allow the System.out.print(n) to happen until n>=0 and so it is recursed n times before the first System.Out.Print(n) happens, resulting in the output being reversed.
That's why it's on the test
Nullius in verba
I am afraid I must recurse myself from this discussion.
This.
The printNumber function is repeatedly called until the base case is reached. Then the deepest called instance returns first (when n<0), reaching the Print statement and returning. As each call to printNumber is popped off the stack, the parent function output value is printed.
Have gnu, will travel.
Recursion works best on data sets where we do not know finite beginnings & ends, and the levels, stages, relationships, etc...are theoretically infinite. A common example is any data structure with a parent/child relationship. This can include a family tree, files on disk, job hierarchy, etc...
Knowing when to use recursion is as important as understanding how to use it. The same goes for iterative approaches. When working with very large data sets, the wrong algorithm can lead to slow, inefficient performance that dooms a project at worst, and slows it down at best.
A very costly common mistake I often see in the real world is when an iterative solution is used in a situation where recursion is the most efficient way to go. Testing with lazy problems like listing the numbers from 0-6 using recursion reinforces potentially bad habits and is almost as bad as teaching recursion using those examples. Simple examples are OK, but should represent the fundamentals that are bring taught.
A better problem for the AP exam would be to provide a problem and NOT tell the students how to solve it. Score the results on if the right algorithm was used and its correctness from there. In the real world, no one tells you how to solve problems, and for a college-level course, the tests shouldn't either.
The question was not how to print "0123456" but what does it mean for a program to call itself and what happens if parts of the program are left hanging, so to speak. If there's a complaint, it might be that the recursive call is not in the tail position.
Explicit stack or queue may allow for parallel optimizations by the compiler.
Stack and heap have a difference with the kind of memory mapping (TLB/MMU) that is possible to be used. It usually doesn't matter until you've reached a large amount of stack memory being used.
Sure, the code for recursive tree traversals looks so much nicer, but they have hidden stack overflow bugs.
32-bit thinking in a 64-bit world? Old habits die hard ;-)
In a tail recursive programming language there is absolutely nothing wrong with using recursion to do things that some people might think is an appropriate use of it. In Scheme, looping constructs are implemented in terms of recursion.
Oh the painful irony.