You may have noticed that several of the old functions computed something and then just printed the answer: the ice/water/steam function, or finding the word for a number, or even the random New Clarkville town generator. Printing the answer is just terrible. We want them to be the answer. The way 2+3 is 5. we want waterState(25) to just be ice.
Our goal is to be able to do things like this:
Our old functions did things. They went all by themselves on a line, like applyColor(1.0f, 0.5, 0);. applyColor doesn’t have an answer or a value, and we wouldn’t want it to.
Our new functions will be part of longer lines, and will work like math:
We already have the rule that functions come back to where you called them. Now that rule means we can pause midway through a line. Above, string w= is waiting for waterState(25) to come back with the result.
The new rule for this is the return statement. It quits the function and returns the answer. Here’s the improved waterState function, using a return:
The print statement at the end is gone, Instead we have return w;. The only thing it does is send back the answer.
Let’s to a quick run-through. s=waterState(31); marks where we came from.
We’re midway through the line, with s= waiting. We copy 31, run waterState, return
"ice" and pop back. Our program now counts as s="ice"; and keeps running from
there.
There’s one more change. The very first thing in the function, before the name, is the type of the return value. It replaces the void. In this case, it clearly has to be string since the 3 possible answers are strings.
Those are the only changes: put the return type in front, and use a return
statement at the end.
The turn-int-into-a-word function can be rewritten. It’s not very exciting, except for how it now works properly. string w=intToString(2) now gets "two":
The 90/80/70/60/50 letter grade if is pretty much the same. The answer is one letter, so a function can return it:
string grade1 = letterGrade(72);. is now allowed. grade1 is a B.
We think of these as “pure” functions – basically like math. They get all input from parameters, and don’t change anything in the program. They just give the answer back and let the main program use it however it likes. These are considered the best type of function.
A fun thing is to write out some real math functions. Some of these are built-in
but they’re fun to write anyway.
Finding the largest of two numbers is usually called max. This function is the same if/else from a few chapters ago, with a return:
If we have float q=max(3,8);, then q will be 8.
This is our first float return-type. It makes sense, since the answer is one of the
inputs, which are floats. It’s also our first returning function with two inputs. A
return can only give one answer, but a function can always have all of the inputs it
needs.
Absolute value (abs) is a fun, short function:
abs(6); is 6 and abs(-12) is 12. This function barely does anything – it’s one if that we could write ourselves. But calling abs(n) is shorter and easier to read, so this is a good function.
In our minds, value-returning functions work like math. Since they don’t do anything, we can run them whenever we want, saving the answer for later. This prints comments about 2 grades:
We might not even use gw2, but it seems easier to compute them both at the top,
the same as we like to do with all of our math.
This uses max to figure out if anyone is old enough to rent a car:
We’re allowed to use these functions inside of larger math. These two add waterState to another string:
There aren’t any special rules here. waterState(270) always becomes "steam",
no matter where it is. 4+waterState(0)+9 is 4ice9, and so on.
These use our new math functions inside a larger equation:
Using the same function several times works just fine. The computer calls them in order, replacing values as it goes:
Here’s a picture of the steps in the last one:
To really check this out, we can add a print inside of max. That’s a terrible idea for real, but good for testing:
Now we can run a line with two max function calls and watch them run:
A fun and completely legitimate trick is using functions inside of an if. Some examples:
The neat thing is, it’s still not a new rule. Same as before, functions are called, run, and count as their answer. Then the line continues.
Here are some more examples of math-like functions.
sign returns -1, 0 or +1. The idea is, it really says negative, positive or zero, but -1/0/+1 is the best way to say that. As usual, the inside is completely boring, except for the return:
For examples sign(6) is 1, sign(-0.43f) is -1.
Previously we saw a pair of if’s to make sure a number is between 1 and 10, or some other range. We can put that in a function:
A common use would be cats=clamp(cats,1,10);. If cats was outside of 1-10, that line fixes it. If cats was 7, the answer is 7 and it changes cats to 7, which seems odd, but it works.
It feels so much like a real math command that people use the word clamp to
mean “adjust into a range”
We often need to get a percent from one number to another. For example 10% of the way from 4 to 7. In math that’s a linear interpolation, abbreviated lerp. The math is easy, but we often write it as a function:
A sample use, our screen from the Cube examples goes from -7 to 7. To
put something 30% of the way across, we can use float xx=lerp(-7, 7,
0.3f);.
Squaring a number is super-easy, just one line, and a new trick:
Notice how we did math after the return. That’s completely legal and works the normal way. We usually think it looks better putting the answer into a variable, unless the whole thing is one equation like here.
To see why this might be useful, consider squaring x+y. square(x+y) looks nicer
than (x+y)*(x+y).
We aren’t limited to real math. Suppose your magical manna points are your Intelligence stat or Wisdom stat, which-ever is higher, plus half of the other one, but at least 5. We can write that:
Even though this is totally made up, it works the same as any other math function. int myManna = mannaPoints(10,5); gives 12. mannaPoints(1,2); is 5.
In the old days if you wanted to know how a game worked, you looked through the code for things like this. Some places called the mannaPoints function, you searched for it, and voila – now you know the formula the game uses for manna.
The return statement jumps out of the function even if it’s not at the end. The function quits right then – any lines after the return are skipped. That should make perfect sense – once you have the answer, the function is done.
You’re allowed to have as many return statements as you need, and they can have any kind of constant or equation after them. Here’s intTostring rewritten using the “return as soon as you know it” method:
That return "three" at the end looks mighty suspicious. There’s no else in
front of it, so it looks like the answer is always "three". But the function works by
abusing the return rules. If n is 0, the function quits right then with answer "zero",
and never gets past line one. It only gets to return "three" if the ifs were all
false.
Here’s max rewritten to have return-abuse:
It doesn’t even have a final return by itself, since the if/else always does
it.
You can use the return-early trick for testing. Here we adding an extra line to temporarily make intToString always say NUMBER_WORD:
Using the trick for mannaPoints shows why we have to be careful with it. Our answer may not be completely done:
If you have a function with no return type (it has void in front,) you’re allowed to use just return; (with nothing after it) to jump back. You can put it at the end, for fun, and can also use return-from-middle.
Here’s just a decorative end return. It’s a little like writing The End:
Here’s the early-return trick on a wrap-around function. It uses the return to quit and not wrap if it doesn’t need to:
You can even use return-from-middle in Start or Update – they’re functions with void return values. Here’s working move&wrap code with return-from-middle:
Normally, Update quits when it gets to the end. Quitting early does the
same thing, only faster. Either way, the “auto-run over and over” rule still
happens.
A use of this trick is to crudely comment out code. This causes Update to do nothing:
Another legitimate use might when something is killed. We can do death stuff to it, then return to make sure nothing else happens:
Obviously return-early can cause lots of confusion if used too much. Your update might have a line adding to x, but x is not changing. After an hour you spot the return up above. The compiler gives a warning when a naked return cuts off the rest of a function, but not for one in an if.
Sometimes it looks nicer to put equations after a return. Like anything else,
the computer works them out first – return 1+3; is the same as return
4;.
Some examples. This abs use two returns, one of them using math:
This redo of lerp does the math in the return line. As usual, it does the math first, then the return:
This mad-lib sentence maker builds a giant string in the return:
If you say you’re going to return something of a certain type, you have to. And it you
say you won’t return anything (by using void,) you can’t. That seems pretty obvious
– a function breaking that rule won’t even make any sense. But there are some funny
cases and seeing the error messages is helpful:
It’s easy to write a function that usually returns something, but for some values it just reaches the end without ever hitting a return. For example this gives compiler error: not all code paths return a value. because of 10 through 100:
It’s a compile error – you can’t even run the program. Maybe you just forgot.
Or maybe you know the input can never be 10-100. The fix is adding a
dummy return at the end. Sometimes I’ll use something like return "BAD
VAL";.
This one’s a little tricker. We can see it always return’s something, but the computer can’t tell. It incorrectly gives the not all code paths return a value error:
Adding return ""; // can’t get here as the last line will make the compiler
happy. Or, rewrite it as a cascading if/else.
When using return; to quit early from a no-answer function, it’s easy to accidentally write return 0;. Zero seems like nothing, but it’s not nothing – it’s a number that means nothing (who knew computer science was so philosophical?):
Returning the wrong type is clearly an error. But the computer will do
any conversions that an = would do. return 3; is fine when you need a
float.
Functions starting with void don’t have answers. Using them inside of math gives an error. This function prints a string, but it has no official answer, so trying to assign from it won’t work:
It says: Cannot implicitly convert type void to int. That’s not quite right, but it
lets you know where the problem is.
Oddly, it’s not an error to ignore a return value. This is legal, but useless:
This might be useful for testing. You might have a bunch of printing lines inside and just want to run it to check what they say.