Trouble using recursion in functions

T

tigrfire

I've written the following function:


void getWager()
{
int wager;
int balance;

printf("Enter wager: ");
scanf("%d", &wager);

if (wager > balance || wager <= 0)
{
printf("Your wager must not exceed your current balance.\n");
printf("Enter a new wager: ");
scanf("%d", &wager);
getWager();
return;
}
}

I would like this function to output as follows:

Enter wager: 5000
Your wager must not exceed your current balance.
Enter a new wager: 50

Instead though, since I use a recursive getWager() at the end of my
getWager function, it displays as following:

Enter wager: 5000
Your wager must not exceed your current balance.
Enter a new wager: 50
Enter wager:

I would like on how to fix this, though without using anything more
complicated than what I have already listed, my knowledge is pretty
limited. Thanks.
 
N

Netocrat

I've written the following function:


void getWager()
{
int wager;
int balance;

printf("Enter wager: ");
scanf("%d", &wager);

if (wager > balance || wager <= 0)

At this point balance is uninitialised and has indeterminate value.

You should probably pass balance in to the function as a parameter.

Recursion isn't necessary. All you need to do is change the "if" to a
"while", remove the call to getWager() and move the subsequent return
statement from within the block to the end of the function
{
printf("Your wager must not exceed your current balance.\n");

You might want to amend your warning statement to take account of the
other condition that causes code to reach this point - wager being less
than or equal to zero.
printf("Enter a new wager: ");
scanf("%d", &wager);
getWager();
return;

Why are you not returning anything? Surely you want to return the wager
as an int?

Likely the answer is that you've copied code other than that which you
compiled. It's good practice to post the minimum code that can actually
compile to be sure you don't introduce problems that distract from those
you're really trying to solve.
 
A

AK

Hi said:
I've written the following function:


void getWager()
{
int wager;
int balance;

printf("Enter wager: ");
scanf("%d", &wager);

Till this point you have not specified any value for balance. Some
compilers take the value to be 0 while others take it to be any random
number in 'balance'.
if (wager > balance || wager <= 0)
{
printf("Your wager must not exceed your current balance.\n");
printf("Enter a new wager: ");
scanf("%d", &wager);
getWager();
return;
}
}

I would like this function to output as follows:

Enter wager: 5000
Your wager must not exceed your current balance.
Enter a new wager: 50

Instead though, since I use a recursive getWager() at the end of my
getWager function, it displays as following:

Enter wager: 5000
Your wager must not exceed your current balance.
Enter a new wager: 50
Enter wager:

I would like on how to fix this, though without using anything more
complicated than what I have already listed, my knowledge is pretty
limited. Thanks.

Does this happen always? If so, in your case I think the compiler took
the value as 0. So, change it. Give an initialization or take that as
argument.

Regards
AK
Owner
Programmer's HQ
http://groups.google.com/group/programhq
 
S

slebetman

tigrfire said:
I've written the following function:


void getWager()
{
int wager;
int balance;

printf("Enter wager: ");
scanf("%d", &wager);

if (wager > balance || wager <= 0)
{
printf("Your wager must not exceed your current balance.\n");
printf("Enter a new wager: ");
scanf("%d", &wager);
getWager();
return;
}
}

I would like this function to output as follows:

Enter wager: 5000
Your wager must not exceed your current balance.
Enter a new wager: 50

Instead though, since I use a recursive getWager() at the end of my
getWager function, it displays as following:

Enter wager: 5000
Your wager must not exceed your current balance.
Enter a new wager: 50
Enter wager:

I would like on how to fix this, though without using anything more
complicated than what I have already listed, my knowledge is pretty
limited. Thanks.

Recursion is not a good mechanism for this. It is not meant to
implement looping because it uses up stack space. Try this instead:

int balance = 10000;

void getWager()
{
int wager;

printf("Enter wager: ");
scanf("%d", &wager);

while (wager > balance || wager <= 0)
{
printf("Your wager must not exceed your current balance.\n");
printf("Enter a new wager: ");
scanf("%d", &wager);
}
}

void main()
{
getWager();
}


If you really MUST use recursion, then try this:

int balance = 10000;

void getWager()
{
int wager;

printf("Enter wager: ");
scanf("%d", &wager);

if (wager > balance || wager <= 0)
{
printf("Your wager must not exceed your current balance.\n");
getWager();
return;
}
}

In the two examples above I used a global variable for balance. In your
code balance is uninitialised so this seems to be what you intended.
But a better code should be something like:

int getWager(int balance)
{
int wager;

printf("Enter wager: ");
scanf("%d", &wager);

if (wager > balance || wager <= 0)
{
printf("Your wager must not exceed your current balance.\n");
getWager(balance);
return wager;
}
}
 
T

tigrfire

I understand what you're saying slebet, but I need the function to
output "Enter a new wager: " whenever the first wager is invalid. This
is the problem I'm having. I can't seem to come up with a good way to
do this, like your first example for instance. The only problem with
that example is that I want to do it without using global variables.

int balance = 10000;

void getWager()
{
int wager;

printf("Enter wager: ");
scanf("%d", &wager);

while (wager > balance || wager <= 0)
{
printf("Your wager must not exceed your current balance.\n");
printf("Enter a new wager: ");
scanf("%d", &wager);
}
}

This is good, except I want to do it without using global variables.
Recursion isn't a necessity either.
 
S

Simon Biber

tigrfire said:
I understand what you're saying slebet, but I need the function to
output "Enter a new wager: " whenever the first wager is invalid. This
is the problem I'm having. I can't seem to come up with a good way to
do this, like your first example for instance. The only problem with
that example is that I want to do it without using global variables.

int balance = 10000;

void getWager()
{
int wager;

printf("Enter wager: ");
scanf("%d", &wager);

while (wager > balance || wager <= 0)
{
printf("Your wager must not exceed your current balance.\n");
printf("Enter a new wager: ");
scanf("%d", &wager);
}
}

This is good, except I want to do it without using global variables.
Recursion isn't a necessity either.

Recursion is a silly way to approach this problem. Just use iteration,
as in the above example. Below I present an improved version of your
program, which demonstrates some other good programming practises you
should incorporate wherever practical.

This new function takes the current balance as an argument, and returns
the wager to the caller. It also checks for invalid input and re-prompts
the user. If end-of-file or a read error are encountered, it will return
-1. However, if the user enters a valid integer followed by other
characters, it will ignore the other characters on the same line.

#include <stdio.h>

int getWager(int balance)
{
int wager;
int ret;
int ch;

printf("Enter wager: ");

/* Flushing stdout is useful to make it more likely
that the prompt will appear before the program
starts waiting for input */

fflush(stdout);

/* Loop while there was a problem reading an integer,
or the integer read was greater than balance, or
the integer read was not positive. */

while((ret = scanf("%d", &wager)) != 1 || wager > balance
|| wager <= 0)
{
/* Read characters until problem reading or newline
reached. This has the effect of skipping any
remaining part of the user's input line. */

while((ch = getchar()) != EOF && ch != '\n');

/* If there is an unrecoverable problem, return -1 */
if(feof(stdin) || ferror(stdin)) return -1;

if(ret != 1 || wager <= 0)
{
printf("Your wager must be a positive integer value\n");
}
else if(wager > balance)
{
printf("Your wager must not exceed your current balance.\n");
}
printf("Enter a new wager: ");
fflush(stdout);
}

/* As above, skip the rest of the input line */
while((ch = getchar()) != EOF && ch != '\n');
if(feof(stdin) || ferror(stdin)) return -1;
return wager;
}
 
E

Eric Sosman

tigrfire said:
I understand what you're saying slebet, but I need the function to
output "Enter a new wager: " whenever the first wager is invalid.
> [...]

Here's a general pattern for all problems of this sort:

do {
display_prompt("Gimme some input, please.");
get_the_input();
} while (! input_is_valid());

This pattern can be implemented in a lot of forms to handle
things like different prompts for the first case and for
the retry, for example:

display_prompt("Gimme some input, please.");
get_the_input();
while (! input_is_valid()) {
display_prompt("Gimme some *valid* input, moron!");
get_the_input();
}

Or, using yet another looping construct:

char *prompt = "Gimme some input, please.";
for (;;) {
display_prompt(prompt);
get_the_input();
if (input_is_valid())
break;
prompt = "Gimme some *valid* input, moron!";
}

Yet another variation:

static const char *prompt[] = {
"Gimme some input, please.",
"Gimme some *valid* input, moron!",
"I'm losing patience ...",
"Last chance. I mean it."
};

for (pnum = 0; ; ) {
display_prompt(prompt[pnum]);
get_the_input();
if (input_is_valid())
break;
++pnum;
if (pnum >= sizeof prompt / sizeof prompt[0]) {
display_error("Don't say I didn't warn you.");
abort();
}
}

All these and more may look superficially different, but
they're really just elaborations of the original, simple pattern:
prompt, get input, check for validity, repeat if invalid. There
is no need for recursion; indeed, recursion would be a bad idea
for this kind of task.
 
S

Skarmander

Eric said:
tigrfire said:
I understand what you're saying slebet, but I need the function to
output "Enter a new wager: " whenever the first wager is invalid.

Here's a general pattern for all problems of this sort:

do {
display_prompt("Gimme some input, please.");
get_the_input();
} while (! input_is_valid());
Yet another variation:

static const char *prompt[] = {
"Gimme some input, please.",
"Gimme some *valid* input, moron!",
"I'm losing patience ...",
"Last chance. I mean it."
};

for (pnum = 0; ; ) {
display_prompt(prompt[pnum]);
get_the_input();
if (input_is_valid())
break;
++pnum;
if (pnum >= sizeof prompt / sizeof prompt[0]) {
display_error("Don't say I didn't warn you.");
abort();
}
}
Eww, degenerate for-loop with error handling inside. I'd expend a
separate boolean for that one.

for (
pnum = 0, input_valid = 0;
!input_valid && pnum < sizeof prompt / sizeof prompt[0];
++pnum
) {
display_prompt(prompt[pnum]);
get_the_input();
input_valid = input_is_valid();
}
if (!input_valid) {
display_error("Don't say I didn't warn you.");
abort();
}

I've noticed this tendency among C programmers to avoid control
variables if at all possible, which often leads to code that's just
slightly convoluted from a structural point of view (notwithstanding
that it might be shorter).

Of course, this version tests pnum one more time than is strictly
necessary. In many cases this happens to be what you want anyway (you
know the prompt array isn't empty in this case, but usually empty or
zero parameters have to be handled).

Here's another version that's shorter and even closer to the original
pattern, but arguably less readable because the error handling comes first:

do {
if (pnum >= sizeof prompt / sizeof prompt[0]) {
display_error("Don't say I didn't warn you.");
abort();
}
display_prompt(prompt[pnum]); ++pnum;
get_the_input();
} while (!input_is_valid());

S.
 
S

Skarmander

Skarmander wrote:
Here's another version that's shorter and even closer to the original
pattern, but arguably less readable because the error handling comes first:

do {
if (pnum >= sizeof prompt / sizeof prompt[0]) {
display_error("Don't say I didn't warn you.");
abort();
}
display_prompt(prompt[pnum]); ++pnum;
get_the_input();
} while (!input_is_valid());

Thinking about this, here's one of those horrible preprocessor hacks
that sounds neat in theory but annoys everyone in practice:

#define EXIT_WHEN(x,p) if(p) goto x
#define WHEN(x) if (0) \
x:

do {
EXIT_WHEN(
tries_exceeded,
pnum >= sizeof prompt / sizeof prompt[0]
);
display_prompt(prompt[pnum]); ++pnum;
get_the_input();
} while (!input_is_valid());
WHEN(tries_exceeded) {
display_error("Don't say I didn't warn you.");
abort();
}

Or, indeed,

#define LOOP for (;;)

LOOP {
EXIT_WHEN(
tries_exceeded,
pnum >= sizeof prompt / sizeof prompt[0]
);
display_prompt(prompt[pnum]); ++pnum;
get_the_input();
EXIT_WHEN(input_valid, input_is_valid());
}
WHEN(tries_exceeded) {
display_error("Don't say I didn't warn you.");
abort();
}
WHEN(input_valid) {
handle_input();
}

All together now: "I'm every language, it's all in C..."

I'm sure someone has implemented this before and I just haven't seen it
yet...

S.
 
E

Eric Sosman

Skarmander said:
Eric said:
Eww, degenerate for-loop with error handling inside. I'd expend a
separate boolean for that one.

Years ago I read an article by Knuth (not "Structured
Programming with Goto," but something else) where he made
the case for a more generalized looping construct than most
languages provide as built-in. His proposal, IIRC, allowed
both the test-and-terminate and the advance-to-next pieces
to appear anywhere within the iteratively-executed body. I'm
sorry I don't recall the examples he provided (nor where I
saw the article), and can't give more detail.

Anyhow, a problem with C's `for' is that the advance-to-
next and the test-and-terminate pieces must be adjacent in
the execution sequence; there's no way to separate them. In
many situations this leads to extra flag variables or to
repetition of the code, both of which I find distasteful.
An empty for(;;) seems to me less than ideal, but better than
one can do with the more "regular" loop forms C provides.

Remember: "De gustibus non disputandum est." (That's
Latin for "There's no point in arguing with Gus.")
 
S

Skarmander

Eric said:
Skarmander said:
Eric said:

Eww, degenerate for-loop with error handling inside. I'd expend a
separate boolean for that one.


Years ago I read an article by Knuth (not "Structured
Programming with Goto," but something else) where he made
the case for a more generalized looping construct than most
languages provide as built-in. His proposal, IIRC, allowed
both the test-and-terminate and the advance-to-next pieces
to appear anywhere within the iteratively-executed body. I'm
sorry I don't recall the examples he provided (nor where I
saw the article), and can't give more detail.
Wikipedia has a nice article on this:
http://en.wikipedia.org/wiki/Control_flow#Proposed_control_structures
Anyhow, a problem with C's `for' is that the advance-to-
next and the test-and-terminate pieces must be adjacent in
the execution sequence; there's no way to separate them. In
many situations this leads to extra flag variables or to
repetition of the code, both of which I find distasteful.
An empty for(;;) seems to me less than ideal, but better than
one can do with the more "regular" loop forms C provides.
I can live with flag variables, as opposed to putting error code
handling within a loop because you don't want to defer the test, which
was my main beef with the posted code. Repeated code is another matter;
I do try to work around that, as it tends to decrease readability as well.

A degenerate for is in itself no problem, if the exit conditions are
clear. I'm not one of those people who rewrites statements because they
don't fit a mold, without stopping to see what's happening to the
readability.
Remember: "De gustibus non disputandum est." (That's
Latin for "There's no point in arguing with Gus.")

I'll say. Opinionated bastard.

S.
 
K

Keith Thompson

Eric Sosman said:
Skarmander said:
Eric said:
Eww, degenerate for-loop with error handling inside. I'd expend a
separate boolean for that one.

Years ago I read an article by Knuth (not "Structured
Programming with Goto," but something else) where he made
the case for a more generalized looping construct than most
languages provide as built-in. His proposal, IIRC, allowed
both the test-and-terminate and the advance-to-next pieces
to appear anywhere within the iteratively-executed body. I'm
sorry I don't recall the examples he provided (nor where I
saw the article), and can't give more detail.

Based on your description, I think C already has this:

while (1) {
...
if (condition) break;
....
if (condition) continue;
...
}

Or was there more to it than than?

(Labelled break and continue would be *really* nice, but we can get by
without them.)
 
J

Jordan Abel

Eric Sosman said:
Skarmander said:
Eric Sosman wrote:
[...]
Eww, degenerate for-loop with error handling inside. I'd expend a
separate boolean for that one.

Years ago I read an article by Knuth (not "Structured
Programming with Goto," but something else) where he made
the case for a more generalized looping construct than most
languages provide as built-in. His proposal, IIRC, allowed
both the test-and-terminate and the advance-to-next pieces
to appear anywhere within the iteratively-executed body. I'm
sorry I don't recall the examples he provided (nor where I
saw the article), and can't give more detail.

Based on your description, I think C already has this:

while (1) {
...
if (condition) break;
....
if (condition) continue;
...
}

Or was there more to it than than?

(Labelled break and continue would be *really* nice, but we can get by
without them.)

The only way C provides to "get by without them" if you need them is
goto.
 
J

John Bode

tigrfire said:
I've written the following function:


void getWager()
{
int wager;
int balance;

printf("Enter wager: ");
scanf("%d", &wager);

if (wager > balance || wager <= 0)
{
printf("Your wager must not exceed your current balance.\n");
printf("Enter a new wager: ");
scanf("%d", &wager);
getWager();
return;
}
}

I would like this function to output as follows:

Enter wager: 5000
Your wager must not exceed your current balance.
Enter a new wager: 50

Instead though, since I use a recursive getWager() at the end of my
getWager function, it displays as following:

Enter wager: 5000
Your wager must not exceed your current balance.
Enter a new wager: 50
Enter wager:

I would like on how to fix this, though without using anything more
complicated than what I have already listed, my knowledge is pretty
limited. Thanks.

Try something like this:

int getWager (int balance)
{
int wager = 0;
int valid = 0;

while (!valid)
{
printf("Enter a new wager: ");
fflush(stdout);
scanf("%d", &wager); // see note below
if (wager <= 0)
{
printf("Wager must be greater than 0 dollars\n");
}
else if (wager > balance)
{
printf("Wager must not exceed your current balance of
$%d\n", balance);
}
else
{
valid = 1;
}
}

return wager;
}

int main(void)
{
int balance = ...; // start with whatever starting balance
...
/*
** Subtract the wager from the current balance
*/
balance -= getWager(balance);
...
}

scanf() really isn't a good tool for doing interactive user input;
given the code above, if anyone fatfingers a character that isn't a
digit and isn't whitespace, scanf() won't be able to read past it and
you'll fall into an infinite loop.

My preferred way of getting interactive input is to read everything as
a string using fgets(), and then validate and convert to the target
type using other tools (in this case, strtol()). Here's an example
(need to #include <stdlib.h> for strtol() and <ctype.h> for isspace()):

/*
** Returns -1 if input is invalid, wager value otherwise
*/
int getWagerValue(void)
{
char buf[129];
int value = -1;

if (fgets(buf, sizeof buf, stdin))
{
/*
** Make sure user didn't type too long a string by checking for
the presence
** of a newline character.
*/
if (strchr(buf, '\n'))
{
char *chk;
int tmp;

tmp = (int) strtol(buf, &chk, 10);
/*
** makes sure no non-numeric characters were entered
*/
if (*chk == 0 || isspace(*chk))
{
value = tmp;
}
else
{
printf("Non-numeric character '%c' found in input\n",
*chk);
}
}
else
{
printf("Input too long\n");
/*
** Consume everything left in the input stream up to the
next
** newline.
*/
while (fgets(buf, sizeof buf, stdin) && !strchr(buf, '\n'))
/* empty loop */ ;
}
}
else
{
printf("Error on read\n");
}

return value;
}

So you'd replace the call to scanf() with the call to getWagerValue()
like so:

printf("Enter a new wager: ");
fflush(stdout);
wager = getWagerValue();
if (wager == -1)
{
printf("Entered wager is invalid\n");
}
else if (wager == 0)
{
printf("Wager must be greater than 0 dollars\n");
}
else
...

Usual caveats apply. I tossed this off the top of my head in about 10
minutes, so there are likely some mistakes in the implementation, but
the ideas should be sound.
 
P

pete

Skarmander wrote:
Thinking about this, here's one of those horrible preprocessor hacks
that sounds neat in theory but annoys everyone in practice:

If defining a macro doesn't make the code easier to read,
then it should be replaced by inline code.
The text that your macro replaces, is easy to read to begin with.
Your macro is just something that neads to be looked up and learned
in order to understand your code.
EXIT_WHEN(
tries_exceeded,
pnum >= sizeof prompt / sizeof prompt[0]
);

if (pnum >= sizeof prompt / sizeof prompt[0]) {
goto tries_exceeded;
}
 
S

Skarmander

pete said:
Skarmander wrote:




If defining a macro doesn't make the code easier to read,
then it should be replaced by inline code.

"Easier to read" is so subjective. I prefer "macros are evil". But I've
already been chastised for this once, so I won't elaborate.
The text that your macro replaces, is easy to read to begin with.
Your macro is just something that neads to be looked up and learned
in order to understand your code.

Exactly.
EXIT_WHEN(
tries_exceeded,
pnum >= sizeof prompt / sizeof prompt[0]
);


if (pnum >= sizeof prompt / sizeof prompt[0]) {
goto tries_exceeded;
}

Sure. But how about WHEN? That one's a lot neater. Those if (0) with
labels aren't that obvious. Also, who could resist an opportunity to
make C look like Ada?

(Don't answer that.)

S.
 
K

Keith Thompson

Jordan Abel said:
Eric Sosman said:
Skarmander wrote:
Eric Sosman wrote:
[...]
Eww, degenerate for-loop with error handling inside. I'd expend a
separate boolean for that one.

Years ago I read an article by Knuth (not "Structured
Programming with Goto," but something else) where he made
the case for a more generalized looping construct than most
languages provide as built-in. His proposal, IIRC, allowed
both the test-and-terminate and the advance-to-next pieces
to appear anywhere within the iteratively-executed body. I'm
sorry I don't recall the examples he provided (nor where I
saw the article), and can't give more detail.

Based on your description, I think C already has this:

while (1) {
...
if (condition) break;
....
if (condition) continue;
...
}

Or was there more to it than than?

(Labelled break and continue would be *really* nice, but we can get by
without them.)

The only way C provides to "get by without them" if you need them is
goto.

Right. Or you use explicit flag variables.
 
R

Richard Tobin

His proposal, IIRC, allowed
both the test-and-terminate and the advance-to-next pieces
to appear anywhere within the iteratively-executed body.

Several Lisps have a loop construct like that.

-- Richard
 
S

slebetman

tigrfire said:
I understand what you're saying slebet, but I need the function to
output "Enter a new wager: " whenever the first wager is invalid. This
is the problem I'm having. I can't seem to come up with a good way to
do this, like your first example for instance. The only problem with
that example is that I want to do it without using global variables.

int balance = 10000;

void getWager()
{
int wager;

printf("Enter wager: ");
scanf("%d", &wager);

while (wager > balance || wager <= 0)
{
printf("Your wager must not exceed your current balance.\n");
printf("Enter a new wager: ");
scanf("%d", &wager);
}
}

This is good, except I want to do it without using global variables.
Recursion isn't a necessity either.

HUH?? Can't you even make such a small modification? I don't like to do
people's homewok. Just pass balance into the getWager function. And
I've shown you how to do this in the other thread.

You really need to learn C. Get yourself a C book or google "c
tutorial". At least go to:

http://www.eskimo.com/~scs/cclass/notes/top.html

and read chapter 5.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Staff online

Members online

Forum statistics

Threads
473,772
Messages
2,569,593
Members
45,111
Latest member
KetoBurn
Top