Teaching students that printf() is not the same as return

26

7

For a few years, I have been teaching a programming class to first-year electrical engineering students -- thus, this is their first contact with computer programming. The language used is C (although this year I started using Logo for the first few weeks as it presents a smaller barrier to entry for a gentler introduction to the practice of programming).

Although the students have issues with many topics of the course, many of them are with more complicated concepts such as recursions and data structures. However, one point that I feel should be easy, yet I'm somehow not getting across to them, is the importance of returning a value in a function.

Concretely, say I want to calculate the norm of a two-dimensional vector, and break it down into the smallest possible functions. So rather than writing something like this:

double norm(double x, double y)
{
  return sqrt(x*x + y*y);
}

I might write something like this:

double add(double x, double y)
{
  return x + y;
}

double square(double x)
{
  return x*x;
}

double norm(double x, double y)
{
  return sqrt(add(square(x),square(y)));
}

Although my students have no problem understanding this breakdown of functions, if I left it for them to write the code, they would probably write something along the following lines, which obviously wouldn't work, and they would be unable to figure out why:

double add(double x, double y)
{
  double sum = x + y;
  printf("the sum of x and y is %lf\n", sum);
}

double square(double x)
{
  double sq = x*x;
  printf("the square of x is %lf\n", sq);
}

double norm(double x, double y)
{
  double result = sqrt(add(square(x),square(y)));
  printf("the norm of (x,y) is %lf\n", result);
}

Note: this isn't actual code that any of my students have written. This is just how I imagine they'd write it if I just sticked to their suggestions during the coding session.

What kind of analogies, objections, examples, counterexamples or strategies could I try to get them to understand that, by printing from a function, the value is not "routed" to the rest of the program, and thus the remainder of the computation will have no access to it?

Extra details

I try to introduce the concept of a CS function by analogy with a mathematical function, say $f(x) = x^2$ (I understand there is the issue of side effects in a CS function, which don't exist in a mathematical function, but I do try to mention this point). I show them how a parameter is passed (in this case $x$) and an operation is performed on it. Passing a parameter is a concept that they grasp easily enough, however most of my students appear somehow unable to understand the seemingly symmetrical issue of returning a value from that function.

Usually, by the point I start talking about functions, I have already introduced the printf() function and shown how it can print the value of variables to the screen.

Then, whenever I propose a problem which uses a function and try to engage the class to help me write code for it, they always get stuck when it comes the point of returning the value from the function. They do understand the importance of getting the value out of the function, but they always suggest I do it using printf() rather than return. Clearly this introduces a problem for the program we are writing because although we can see the intermediate results, there is no (sensible) way to consume the output stream in the rest of the program.

Although they do understand the importance of getting the computed value out of the function, they seem to think it's OK if it's just printed with printf() rather than properly returned from the function. I try to object that once something is printed to the screen, although it does show you, the programmer, the value in question, the computer can't "read" a printed value back from the screen gives that it's now just an assorted bunch of pixels on the screen. I even tried to make some kind of analogy with trying to present a picture of a written word to Google (say "programming") and having Google search for that word. However, somehow they don't just get this point.

swineone

Posted 2017-10-04T16:19:46.850

Reputation: 241

There seem to be two issues conflated in the question. One is structuring a program to be modular, the second relates to the concept of pipelining several individual programs. I guess that the question would be equally valid without the STDOUT part. Or maybe you mean to include this as your current teaching example which is not as effective as you want? – Sean Houlihane – 2017-10-04T16:30:09.133

Indeed, I try to impress upon them the importance of modularity, otherwise I might just say it's OK to create gigantic main() functions with the whole code there (and of course, it's impossible to teach something like recursion without resorting to functions). The main issue indeed is teaching the necessity of pipelining the output of a function to another piece of code. They seem to think that, somehow, once I printf() something, the computer "understands" this is what they mean to return from the function, but obviously it doesn't work like that. – swineone – 2017-10-04T16:36:12.837

Maybe you can update the question to identify your process more specifically, so questions address the right detail. It's not always easy for people from different places to answer the question you thought you asked! The issue does resonate with my early coding experiences. – Sean Houlihane – 2017-10-04T16:42:38.763

I'm not entirely sure what I should change in the question. Perhaps you'd like me to stress this point of pipelining function outputs to other code? – swineone – 2017-10-04T16:44:24.920

I edited - mostly re-ordering two paragraphs. So I think now you might need to also share the example problem. I have a feeling it might be too similar to your previous examples. If you do print (f(i))) then its different to for (::)i+=f(i); print(i) – Sean Houlihane – 2017-10-04T16:55:45.507

1OK, I added a concrete example. – swineone – 2017-10-04T17:01:12.373

16Why is this such a long question? Students cannot seem to differentiate between return and printf. Then, give an example. Please edit out all the noise. Meanwhile, let me share in the answer section what I did to solve this. – Jay – 2017-10-04T18:13:40.667

6This misconception isn't special to students studying C. It is common in beginners who don't understand the operation of a machine separate from its operator. – Buffy – 2017-10-04T18:25:56.993

1Note: some languages do it the way the students think it is done. That is you return data by printing it. Examples are the Unix shells. – ctrl-alt-delor – 2017-10-04T18:28:36.933

5@Jay Sorry for the question being so long. I guess it is my style to try to be as thorough as possible, and also to point out the context of the issue and what I've tried. I've edited the question to add a heading "Extra details" and tried to keep the core of the question before that heading. – swineone – 2017-10-04T18:33:05.357

Its long dude. So long. Live and let live is my motto...so it should be okay :) – Jay – 2017-10-04T19:12:18.583

4I remember that this was one of my big "aha" moments. My reasoning for why it was hard to grasp back then was that I was coming to it from BASIC, which had no concept of scope. Once I understood scope, the role of "return" became obvious. – pojo-guy – 2017-10-05T01:48:00.263

Draw a diagram of the data flow with boxes and arrows. Show each function as a box. Also show standard out as a box. Point out that when all of the data just goes to standard out, norm never gets the results of square and has nothing to pass to add. – Ant P – 2017-10-05T13:16:24.117

@ctrl-alt-delor Check you /bin/sh manpage for what the return keyword does. A shell function returns to the calling routine in very much the same way that a function in any other language does, and printing to the user is not returning a value in that context any more than it is in c. – dmckee – 2017-10-05T22:50:02.860

@dmckee /bin/sh etc, return only returns an 8bit error code. You have to print/echo to stdout, to pass data to the next function e.g. function a() { echo "hello, world"; }; a | grep "hello" – ctrl-alt-delor – 2017-10-06T08:03:44.113

Get them to compile their code with -Wall -Werror -pedantic? The compiler won't let them do this. – Roger Lipscombe – 2017-10-06T09:06:39.660

1I don't have enough rep to answer, but one neat idea an early CS professor did was assign groups of students to play the role of "function". If your team was "called", you had to calculate the result. But just calculating it wasn't good enough; if the team "calling" you needed that result, you had to write it down on a piece of paper and "return" it to your "calling" team. You could demonstrate the differences between return and printf in concrete role-playing this way. – MrDuk – 2017-10-06T15:36:45.153

An actual example: link

– hoffmale – 2017-10-07T11:05:03.023

It's been over a decade since I taught CS, but @MrDuk's professor's technique is exactly what I did to illustrate functions and scope. Get the students off their laptops and have them physically participate in an analogy. It's amazing how well this technique helps stuff sink in. I taught working professionals late in the evening (they were exhausted and had attention issues) so sticking to straight-up lecturing was a recipe for failure. – Katharine Osborne – 2017-10-09T11:07:33.077

Answers

30

I think this is a problem where the answer is partly in the prevention. I've observed many classes where printing is the method used to access the results of calculations for several weeks, both within functions and within main(). Then, return values of functions are suddenly introduced, but, they only serve to pass the result to main to be printed once again. No wonder students think they are the same---they are functionally equivalent in just about every example!

  • Setting a standard that printing only happens in main() may help. Good coding practice too.
  • Talking about return values of functions early, and using sample tests (e.g. "What is returned by my_func(4)? By my_func(0)? By my_func(-4)?") may help.
  • Showing that it's possible to write code without printing, such as on CloudCoder or in Websheets, may help.

Good luck!!

nova

Posted 2017-10-04T16:19:46.850

Reputation: 1 699

Actually, for this year, I have decided to delay as much as possible introducing printf() to them. Sometimes I will provide code samples where it is used, but I did not formally introduce the concept to them. When someone does ask about it, I reply in jest that it is a very dangerous concept that made quite a few former students fail the discipline previously (which, in a way, isn't far from the truth). Unfortunately, having seen it in action a few times, they've already realized how it works and keep trying to use it. – swineone – 2017-10-04T22:42:52.960

@swineone Interesting. If you're using C, how can the students see their results without printing? – nova – 2017-10-05T01:46:18.750

4As I mentioned somewhere else, I started with Logo, so many things can be done without printing (although, admittedly, drawing poses the same problems as printing). I'm introducing the subject of C to the class now and I'm trying to use the test-driven development methodology, where for now I will be supplying the tests and skeleton functions for them to write their implementation. Also, I plan on teaching them how to use the debugger as an alternative to printing. – swineone – 2017-10-05T03:39:45.240

With respect of print only in main. I have a rule, that functions only call functions. Procedures can call functions or procedures. print is a procedure. main is a procedure. Therefore I can write other procedures to print, but not functions. (a procedure does something, a function returns something.) – ctrl-alt-delor – 2017-10-05T07:52:56.913

"Setting a standard that printing only happens in main() may help" I would hesitate to agree with @ctrl-alt-delor... I would say main should call a calculate function which stores result... and then sends result to a separate function that prints - neither function is "main". Then a third function that waits for enter to continue. main(), int results(int a, int b), void printresults(int a)... separation of concerns. Each block does one thing. 1) Main coordinates, 2) results calculates, 3) printresults prints... results. – WernerCD – 2017-10-05T13:01:24.700

@ctrl-alt-delor main is a function, it does return int (exit status). It's just that in C99 you may omit the return statement, with implicit return 0;. – Ruslan – 2017-10-05T13:34:45.017

@Ruslan printf is a procedure with a (status) return value (so not pure). Therefore should only be called from a procedure. (this is not a rule of C, just one that will help with your sanity.) – ctrl-alt-delor – 2017-10-05T14:35:33.320

2@ctrl-alt-delor you seem to be inventing your own terminology here. If printf returns a value (which it always does) then it's a function. The fact that many people choose to ignore the return value is a different issue, and no different conceptually from writing int i, j, k; j = 1; k = 2; i = j, k;. – alephzero – 2017-10-05T22:40:12.523

For those that have a different idea of what a function is, a rewrite:

With respect of print only in main. I have a rule, that side-affect-free-subroutines only call side-effect-free-subroutines. Subroutines-with-side-effects can call side-effect-free-subroutines or subroutines-with-side-effects. print is a subroutines-with-side-effects. main is a subroutines-with-side-effects. Therefore I can write other subroutines-with-side-effects. to print, but not side-effect-free-subroutines. I also ensure that only side-affect-free-subroutines return a value. Error codes, excepted in non-OO. – ctrl-alt-delor – 2017-10-06T07:52:00.793

@swineone Since you will already be providing the skeleton, make the way the receive feedback be a GUI that was just a couple of input boxes/labels. I remember my professor having us create a Java class with a specified set of functions, then he would run our class against his test GUI program that was only missing our class. Substitute for a shared lib/dll in your case. – Core.B – 2017-10-06T14:11:26.370

2@ctrl-alt-delor Maybe I'm being pedantic but those aren't just your rules, those are the rules of reality. As soon as you try to have a side-effect-free subroutine call a subroutine with a side effect the former is no longer a side-effect-free subroutine. No rules have been violated. Side-effect-free subroutines necessarily don't call subroutines with side effects. Its part of what makes them side-effect-free. – Matt – 2017-10-07T23:13:04.383

@matt I noticed the same when I write it out in the long form. When I have presented it in the short form. People say I am wrong. Then when I present it in the long form it is just a tautology. Why do we fight it? – ctrl-alt-delor – 2017-10-08T17:36:31.113

@ctrl-alt-delor Probably because your short form implies C makes a distinction between "functions" and "procedures" which it does not. When you frame it in terms of C it seems like a rule of C (but it's not actually a rule of C, hence you are wrong). When you frame it as a general design concept it is correct. However I assume students at this level cannot yet grasp the general design concept (or perhaps even the idea of software design) so it's not useful. – immibis – 2017-10-10T03:52:25.743

@immibis sorry, it is not a rule of C, or of many languages. Just a rule that I have picked up, that makes programming easier. You can however configure a lint tool to do the checks, at least for C++. C++ has the idea of side effect free methods (almost), but the lint tool can be made to be more strict. I would also argue that all examples should be best practice, even if you don't teach the best practice directly. – ctrl-alt-delor – 2017-10-10T08:38:08.660

17

I think that this problem is pretty widespread, actually. I believe that it comes from a misunderstanding of the difference between what the program/computer can know/do and what the person operating it can know/do. I think the problem is worse for interactive systems where the printf sends its output to the screen in front of the person running the program.

Many students with this misconception will print the output of a function and then write code to ask the user to type it back in so that the computer can "learn" the earlier result and then continue.

It was more obvious that it didn't work in the (very) old days when print sent its output to a chain printer in the back room of the computer center and you had to go ask for it. **

It might be enough to just explain this to them explicitly, but maybe not.

So, you might have a little "active learning" exercise like this. You will want four "players", one for the operator, one for a function (main) that calls others, the third for a function (sum) and the fourth for another function (product) .

The operator wants to know the value of 3 * (4 + 5); It/he/she asks main for the result. Function main will actually print (pseudocode - omitting details)

main()
    x = sum(4, 5)
    y = product(3, x)
    print(y)
  • sum(a, b) wants to compute the sum of a and b and "do something" with it.
  • product(a, b) wants to compute the product of a and b and "do something" with it.
  • print writes a value on a piece of paper and hands it to the operator.

Now there are two versions of sum and product; like this (only sum is shown):

sum(a, b)
    return a + b

and

sum(a, b)
    print(a + b)
  • return x is implemented by writing a value x on a piece of paper and handing it back to the person who asked you to execute (this will be main in each case), whereas print x hands the paper back to the operator.

Now run the simulation with each version of sum and product. Note how with the printing version of sum, the paper goes back to the operator and the main (function/person) doesn't have any value to continue with so it gets stuck. With the return version, the sum goes back to main and so it/he/she can continue. Only one paper is ever handed to the operator and there is no getting stuck.

Write out the program fragments on index cards and give them to the players. The "sum" player has only one version of the function in each simulation. The "main" player has the code for main above.


Note that print and return always operate the same - paper to operator vs paper to caller. The operator actually asks main for an operation: "Main run".
Player "main" actually asks sum and product for an operation: "sum 3, 4"

I've found it helpful to have sticky notes with the role names that the players can stick on their shirts.

It is probably necessary to do the return version first. This will make the contrast more clear. You may have to run each more than once, and you may have to talk through the "execution". You should almost certainly ask for questions.


Let me note two things about this idea:

  1. Lots of misconceptions can be dispelled with a "play acting" active learning exercise like this. "Play the computer" is often helpful.

  2. Doing this proactively, as part of introducing return or print is probably not a good idea. Most students will "get it" from a simpler and shorter explanation. So this is the sort of thing you have in your kit bag for emergencies.

Buffy

Posted 2017-10-04T16:19:46.850

Reputation: 21 033

7The play-acting idea sounds fantastic. I wonder if it might help to "implement" return by whispering rather than passing a paper? Or alternatively, make print() write the result on a chalkboard visible to the class (not the actors)? The idea being to show print() and return as being very different actions. – David Z – 2017-10-05T01:04:31.623

Many variations are possible. Just think it through before you implement it. And expect surprises. Things can go wrong, so be prepared for a backup explanation. – Buffy – 2017-10-05T10:41:22.187

make them chaining the calls, that's exactly what I though when I red the post. RIP printf solution. – Walfrat – 2017-10-05T14:51:49.183

11

This is such a consistent trap that I ultimately created a worksheet to deal with it. At this point in my course, I have recently covered binary and hexadecimal, so I also use this worksheet as an opportunity to gently review those concepts.

My worksheet may not be as physically active as some of the lessons that have already been posted, but it is highly practical. The entire activity takes 10 minutes at most, it reviews some additional concepts, and (because the distinction being made is fairly simple) they never seem to get confused again afterwards. It could also be given as a very easy homework assignment.

The top reads:

Sometimes we use System.out.println, and sometimes we use return. Remember the difference? Returns are for giving information back to the computer, while printlns are for giving information in text form directly to the human user.

Consider the following scenarios. For each scenario, if it would be appropriate to use a return, write 1. If it would be appropriate to use a println, write 0. At the end, please write out the secret hexadecimal message!

What follows are 16 short questions in a table. And just to give you a flavor for the kinds of questions asked, the first question is:

In a 3D rendering program at an animation studio, a method calculates a double representing the exact value of brightness of each pixel in the frame.

The final message is the hexadecimal number f1ea, which is nice because fleas are so cuddly it is easy to check. (As an added bonus, kids who answer incorrectly are forced to decode the hex back to binary to figure out which questions they need to reconsider.)

Reviewing the worksheet in class takes almost no time at all. The kids get a kick out of the secret message, it reviews a prior concept, and (as I mentioned above), the entire activity is quite short.

Ben I.

Posted 2017-10-04T16:19:46.850

Reputation: 17 660

8

Whenever I run into students who are baffled by the return concept (and in your case, them substituting it with printf), I do something like this.

I use role-play. I become one function (lets say the main function) and the student is another function (lets say some function add that adds two numbers and returns the values).

Here is how the role play works.

  1. I write two numbers on a piece of paper and give it to the student. In the code, this is like, I am the main function calling the add function and sending two values.
  2. Now, the agreement is that the student cannot just shout the answer (this is like printf in the code)
  3. Since shouting is not an option, the only way the student can let me have the answer is to 'return' me a piece of paper which contains the answer, which is the total of the two numbers I gave in step 1.
  4. So, the student returns me the piece of paper. This in the code becomes, the value being returned.

What I do is, I emphasize the fact that printf is like shouting, which does not help me as I need the answer on a piece of paper that must be returned explicitly.

Jay

Posted 2017-10-04T16:19:46.850

Reputation: 1 612

I can see that making it physical does help with the idea of 'message passing' and even mailboxes, but this seems very close to the described 'didn't work' option. – Sean Houlihane – 2017-10-04T18:49:54.037

1Worked for me for 5 years and over 5000 students. So, yeah, it works :) – Jay – 2017-10-04T18:55:36.817

I like it, You could ware ear defenders, to show that you can not hear. But what is “comparison printf”? – ctrl-alt-delor – 2017-10-05T07:56:51.937

oh man, I messed that sentence up. fixed it :P – Jay – 2017-10-05T11:35:40.393

This is great, and +1, however I would like to make a suggestion for it. Perhaps explaining to the students that computers are dumb, and they cannot intuit on their own what to do with things that you display on screen or audio played. Likewise, you, being the computer for the role-play, only understand what is in your computer memory, and the paper is your computer memory. Your microphone might hear the shouted answer, so I would not even discourage shouting the answer, but when they expect you to react they get "I'm a PC and not programmed to respond to audio; put it in my computer memory." – Aaron – 2017-10-05T12:14:44.627

Further; with my above suggestion, inevitably someone will ask "Then why don't we just program the computer to understand and use the audio or visual data so it can just pick up on the printf and know what to do?" To which you can reply "Billions of dollars are spent trying to do that. OCR, speech-to-text, artificial intelligence; that is required for what you suggest. It is possible, but it does not work as well, and it eats a massive amount of processing power... when you could just explicitly return this value." – Aaron – 2017-10-05T12:17:37.197

1It just so happens I do a lot of what you said. I pretend I am a computer and then, do literally nothing until I am explicitly told. I can be pretty dramatic at that point to drive home the point that computers are fast, efficient but totally just dumb. – Jay – 2017-10-05T12:23:58.367

7

Have you considered not teaching them about printf at all?

99% of the use of printf is as a poor-man's debugger. Teach them how to properly use a debugger to step through their code, and inspect their variables, rather than defensively printing out everything all the time, and doing post-mortems on log output.

Alexander

Posted 2017-10-04T16:19:46.850

Reputation: 171

Real programmers don't use debuggers (or printf). OK I do sometimes. However when I use design by contract, and/or test first. I don't need them. I have not done these in a teaching environment (yet), but they are very powerful, and result in code that work, and stays working. No need to debug. – ctrl-alt-delor – 2017-10-05T08:06:22.537

5I am firm proponent of caveman debugging. printf should be okay. – Jay – 2017-10-05T11:37:28.290

Debuggers are a very bright almost laser-like spot light. You can view one aspect of the code (in space and time) very vividly. But you don't get a full picture. You have to shine that light in a lot of places to understand the full picture - this takes time. On the other hand, print statements are like flash lights. You can get some understanding of the entire picture but not all the details. Most of the time, flashlights work well. – Harrichael – 2017-10-07T16:13:59.307

@Harrichael Breakpoints can execute arbitrary debugger instructions, like printing variables, without polluting your release application – Alexander – 2017-10-07T16:21:59.153

I'll keep that in mind, but I would definitely recommend removing print statements after debugging - I would shudder at leaving them in. – Harrichael – 2017-10-07T16:28:07.793

@Harrichael That's a lot of work, that's what logging frameworks are for, to give you a central control that lets you disable all (for example) "info" and "debug" level messages – Alexander – 2017-10-07T16:37:51.527

5

The first metaphor that comes to mind is language; functions can act as verbs or nouns. So, for example, we can have:

double print_sum(double x, double y)
{
 double sum = x + y;
 printf("the sum of x and y is %lf\n", sum);
}

We can then have a line that says “print_sum(1,2)”, and the computer will print the sum of 1 and 2. Here, “print_sum” is a verb, and the parameters are the objects of that verb (the implicit subject is the computer, of course). Thus, saying “print_sum(1,2)” is a complete sentence. “print_sum” is an action, and putting it in your code tells your program to perform that action.

On the other hand, we can have:

double sum_of(double x, double y)
{
 return x + y;
}

Here, “sum_of” is a noun. “sum_of(1,2)” is not an action, it’s a thing. Having a line that consists of nothing but “sum_of(1,2)” wouldn’t do anything; it’s not a complete sentence. However, it can be the object of a verb; for instance, “printf(sum_of(1,2))” would be a complete sentence.

Advanced programmers naturally go back and forth between treating functions as verbs or nouns, and sometimes use them as both (that is, they use the return value in a calculation while also taking advantage of their side effects), although many people frown on such behavior. Your students clearly aren’t able to distinguish these two attributes on their own, so you need to make it more explicit. Naming a noun-function “add” really isn’t helping; that’s a verb-name, and so your students are going to be expecting it to be used for its effects, rather than its return value. If you were explaining in English how to calculate the norm, would you say “Take the square root of add the square … “? Have your students pay conscious attention to whether a function is being used as a noun or a verb, have them give them appropriate names, and model this by giving your functions appropriate names.

If a function is being used as a verb, then you can just run the function, and whatever code is in the function will be run. If you’re using the function as a noun, however, then you need to tell the program what value to assign to the function. That’s where the “return” command comes in. The “printf” command is still in verb-land. It’s doing something, not being something; if you write printf in sum_of, you’re telling the program what sum_of does, not what it is. When you write “return x+y”, on the other hand, you’re telling the program that “x+y” is what sum_of is.


Another metaphor is an office worker with an in-box and an out-box. The worker corresponds to a function, the in-box corresponds to the parameters, and the out-box corresponds to the return value. We can also imagine something analogous to printf, such as the company twitter account. When students put printf in a function, and then try assigning something to the function, the analogous situation is a worker tweeting the result of instead of putting it in their out-box.

Acccumulation

Posted 2017-10-04T16:19:46.850

Reputation: 191

Brilliant explanation of command query separation, and of naming methods. The only thing that I would add is adjectives, for when a method returns a boolean. Then more advanced adverbs or adjectives for enums (you should never pass a bool into a method, it will be, humanly, unreadable). (I have used the word method here, to mean subroutine, as I use function as a query, the noun/adjective thing, and procedure as a command a verb thing.) – ctrl-alt-delor – 2017-10-05T17:48:18.637

1+1 function does what the name says it does!! sum() doesn't say print so it shouldn't print! – RonJohn – 2017-10-05T21:41:50.110

@RonJohn that might not work because: it also doesn't say return so it shouldn't return! – immibis – 2017-10-10T03:55:08.750

@immibis that's so inane, I lost 3 IQ points just reading it. Do you know the slightest thing about math, and what functions even are? – RonJohn – 2017-10-10T04:21:27.020

@RonJohn Do your students, who you are trying to teach the difference between print and return to, and who probably do not understand mathematical functions any better than they do programming functions, know the difference between print and return? – immibis – 2017-10-10T04:38:22.660

@RonJohn And if they do not understand the difference between print and return, why do you suppose they understand the difference between "it doesn't say print so it shouldn't print" and "it doesn't say return so it shouldn't return"? – immibis – 2017-10-10T04:38:58.037

@immibis Why are students who barely -- if at all -- understand math being taught how to program a computer (that thing who's raison d'etre is doing math)? – RonJohn – 2017-10-10T04:55:44.697

@RonJohn Why are people who barely understand Maxwell's equations being taught how to paint a picture (that thing whose entire purpose is to intricately manipulate electromagnetic waves)? – immibis – 2017-10-10T20:34:56.377

@immibis completely flawed logic, in that people have been paining for at least 32000 years, while -- as I wrote -- the whole raison d'etre of the computer, and it's baked into the very name -- is math. – RonJohn – 2017-10-10T20:50:24.570

Let us continue this discussion in chat.

– immibis – 2017-10-10T21:54:30.647

4

The conceptual issues here are that the examples in the OP are completely artificial (intelligent students should be asking "why on earth would you want to write a function to add two numbers, in C, when the language already contains a + operator") and that they only call the function once.

You could solve the first issues by a digression into operator overloading (which a student intelligent enough to ask the question should be able to understand!).

For the second issue, invent a situation where you call your function many times, but only want to print out one "answer". It should then be clear to the students why you don't want 1,000 lines of useless print coming from inside 1,000 calls to your "add" function!

alephzero

Posted 2017-10-04T16:19:46.850

Reputation: 603

1I imagine you could also solve the first issue by using a more complex function... such as the absolute value (provided they don't yet know it's built in) or vector length. – immibis – 2017-10-10T04:01:08.340

3

After doing @Buffy's active learning, the next step would be.

For you to to create the program, and remove the guts of the functions. Just the skeletons for the students to fill in.

int main(){
  print norm(4,7);
}

double add(double x, double y)
{
  return 0;
}

double square(double x)
{
  return 0;
}

double norm(double x, double y)
{
  return 0;
}

They have to take your code and change the 0s to the correct code.

Parsons problems

Give them all of the correct code, but in the wrong order. They have to rearrange it.

Pupils can do a lot of these in a short time. Each one is, supposedly, nearly as powerful as doing a normal programming exercise. However you can do more of them, and there is less frustration.

ctrl-alt-delor

Posted 2017-10-04T16:19:46.850

Reputation: 7 268

3

However, one point that I feel should be easy, yet I'm somehow not getting across to them, is the importance of returning a value in a function.

Then focus on that.
I know, you think you are doing that (so much, that you've gone to the extent of asking here about that). However, you're not, as I will demonstrate.

People love to have their needs fulfilled. So, give them some clear needs.

Teachable moments:

First, I'd like to point out some possibly-great teaching moments that this code offers, so you don't miss this opportunity:

if I left it for them to write the code, they would probably write something along the following lines, which obviously wouldn't work, and they would be unable to figure out why:

(3 functions that each "printf")

Looking at those examples, the "square" function takes a value of x, and the printf you wrote states, printf("the square of x is... Later, you call square(x),square(y). I'm guessing that some (and perhaps many) students would be perplexed by the result. When you call "square(x)", it says "the square of x is". However, when you call "square(y)", the code says "the square of x is". Some students might not understand the discrepancy (of why the code says x when it refers to a value which is clearly identified as y), which they may not have noticed until you point it out (because when they read the source code of the square function, everything looks like an x and so it makes sense. But then you point out something that doesn't make as much sense, and they may wonder why it doesn't make sense.)

This conundrum could be addressed by focusing on the situation in 3 different ways:

  • the passed value ("when we say x, we really mean the first parameter"),
  • or the difference between "x" as a variable and "x" as part of a literal string,
  • or looking at the "scope" of a variable, pointing out that we have multiple x's, and so the y in the norm function becomes the x of the square function, and that doesn't affect the x in the norm function, because references to x are not affecting a global variable named x

So there are multiple teachable points to bring up.

Simplification Helps:

Now, another item I'd like to note about this code...

You're also complicating things by taking add()'s return value and passing it to sqrt(). So you have a double-call, which may be harder to grasp than a simpler example. You could simplify the example simply by storing add()'s results to a variable that will then get passed to sqrt(). For experienced experts like us, that looks like an extra variable and needless complexity that makes the code longer. However, breaking it into separate steps, where each step is short and direct, is actually simpler and will be easier for people to grasp.

Part of the reason I'm pointing this out now is so I don't need to explain it in the middle of my upcoming finale (to reduce how much I jump to one topic in the middle of explaining another topic).

People love to have needs solved:

Now, I'd like to get back a bit more to focusing on the solution to your base question.

I point out that your students may be quite "right"... from their perspective. And that may be why they are struggling to grasp what you're trying to demonstrate/teach to them. Their paradigm may need to be shifted.

What is the goal? If they think the goal is "to get the computer to print out the needed data", then it is very sensible for them to want to "print out data as soon as we have it, and then have no more use for the data" and allow the data to be discarded without being returned. That actually seems more efficient than trying to retain the data just to do work later. They may be following some principles of efficiency, which might actually be a good thing.

I suggest that the solution might be to provide another example. Demonstrate the use of a returned value. Note: I did not just say, "demonstrate a return value being used". You technically do that, as the norm() function's add() call uses what gets returned by "square". (And you actually do that twice, as I noted earlier when I mentioned you're doing a complicated nested/double call.) What I'm saying is to clearly show just how the concept ends up being useful.

Take the returned value, and demonstrate using the returned value multiple times. First takes the results of square(x), and pass that to add(). However, then do something else useful with x. (Print it out again, in another context. Maybe perform a test of some sort, and ensure that is done after the value would be corrupted by an earlier step.)

Then they will see why the main() function needs to keep track of x. The simple reason is that the value of x gets used multiple times, over and over again. They can't just "print and forget" x, because the value of x because a valuable thing, and therefore must be preserved and protected.

If you want the students to understand the concept of a function returning a value, then give them a reason to see a need for the concept being effectively used. As described in the prior two paragraphs, that can be very simple in nature. When you can show them a simple-to-see example of how it is being used effectively, their natural instinct will be, "Well of course that's how it works. That would be the simple and straightforward method of accomplishing exactly what I see is being accomplished." Once they see those mechanics being clearly used, they will more readily understand how the mechanic operates. Then they may be more open to understanding just how that can be effectively useful in different scenarios (which hopefully you can demonstrate with some more prepared examples).

Syntax Error:

Note: the code you showed is buggy. Well, we knew that. The desired point of the square function is to make sure that x is usefully being returned, and the problem you point out is that the example code just tries to use x immediately without returning it. A possibly-great thing to do is to show the problem, by demonstrating what happens when x isn't returned, and then x not working as designed in a later location when x is needed. However, you may not be able to effectively demonstrate that bug, because there is another bug. The code just simply won't run at all, because the C complier is going to complain that the "square" function is declared to return a double, but then there isn't a return statement. Since you have a syntax error, you won't be able to get cooperation from the C compiler, which means you won't be able to run wrong code that demonstrates the logical flaw in the code.

You may want to correct the code so that it looks better, and instead of the compiler visibly reporting a problem, you end up having runnable code that is flawed. (Instead of an obvious error, you have an error that is more difficult to notice. That's actually worse. Demonstrating that can be another great thing that you can teach them.)

Review/Summary:

  • The key answer to your question is: demonstrate the worth of having x being returned, so that x can be used multiple times by the calling function
    • That's the simple core of the answer that directly addresses your main question
  • I see several other opportunities of bringing up some topics that would be good to point out to novices, and so I've pointed them out so that you can take advantage of these instead of missing any of the ones I noticed.
    • The different ways that the symbol "x" gets used provides multiple teachable moments.
      • To keep this summary shorter, I don't repeat every one of those topics, but just look for the prior bullet points, because each one of those bullet points represents an important topic that is worth independent discussion.
    • Try not to do any more than one function call at a time until they understand the mechanics of the life cycle of calling a function, including the return value
    • You've positively set yourself up with an excellent opportunity to demonstrate how a very-wrong error can cause visible complaints by the compiler, but a sneaky error may provide okay-looking wrong values, which can be harder to notice and are, therefore, even worse.

TOOGAM

Posted 2017-10-04T16:19:46.850

Reputation: 221

Long, but very useful. – Sean Houlihane – 2017-10-05T11:58:11.450

3

I have found the key is to illustrate the difference between side-effects and returns.

When teaching functions, I do an exercise called "guess the function" where I tell students to write a number on a post-it and hand it to me. I then add 3 to the number, write it on a new post-it, and hand it back to the student. I repeat this a few times with squaring the number and other slightly more complex operations, always handing back to the student a post-it with the result.

Next, I'll chain together two functions using a student volunteer who I instruct to add one to the number. I then square it and hand it to the original student.

After several rounds, when I feel my students have become comfortable with the pattern, I change my behavior. When a student hands me a post-it, I write "Your number was " on the board, substituting their number. I also take the original post-it and ceremoniously throw it in the trash. I repeat this with different operations (division, etc). I ask a student to explain the difference to me.

Students are now primed for an explanation of the difference between returns and side-effects. They see how I can't chain together two actions when I write a number on the board, but I can when I write the number on a post-it. It's a worthwhile 10 to 15 minute exercise.

Nate Vaughan

Posted 2017-10-04T16:19:46.850

Reputation: 131

2

My guess is that the gap in the learning comes from not recognising what variables are and the concepts we're familiar with for data storage. I can imagine that for maths/engineering students, the fluidity and connected nature of equations makes the problem worse.

In circuit design for example, modules will be familiar. These are blocks that have interfaces, some inputs, some outputs, some power. The operation of the module is a continuous process, connections being made with wires.

There are two connected parts that I think will be useful to show to your students about what a computer program is trying to represent (and the fact that it's an abstraction might be something to explain to them).

Firstly, computers perform basic steps one at a time (or in blocks, or several at a time, but always in a limited scope). Think of buckets of water rather than a hose pipe.

The second is the concept of doing the work on individual blocks of data. This data has to be stored somewhere. It could be the water in your buckets (which can be split up, coloured, mixed, etc). Computers don't do mixer taps, they have to take the items they're working on piece by piece and work on each element. If they know about integration using calculus, the difference between discrete and continuous items shouldn't be a problem.

I think if you review the way you're teaching variables, also show that to process code, a computer (a general purpose machine) follows its instructions to operate in steps, it will be easier to show that the result of an operation always needs to be stored somewhere (not just for functions), so a program is different to a circuit. An electrical module is fixed at it's location in a circuit, but a software function can be called from different places.

In a program, whenever a function is called, there is a place defined to put the result. This isn't 'in' the function, it's 'in' the code that calls the function. (of course, if it's not used, then the computer knows it doesn't actually need to bother with it).

I'm not sure you need to have your students moving the data, but showing containers for data does help. The numbers in envelopes fits best if you use it to show how add, multiply etc. work - before you get to the functions. Playing out the operation of the machine requires a few more steps to make it complete, if you act it out, then you can avoid some of the details that they don't ask about...

Sean Houlihane

Posted 2017-10-04T16:19:46.850

Reputation: 1 429

2

I want to give a really simple solution.

Let 2 students stand up, student 1 = s1 = caller, student 2 = s2 = function

Tell one to take a pen with a cap and give it to s1

s2 is the function.Tell s1 to give the pen to s2 ("parameter")

s2 has to remove the cap ("function")

Now you have 2 options to tell the s2, aka the "function" what to do after executing, return or printf

if s2 hands the pen with removed cap to the s1, he "returned". If s2 tells s1 what he did it's "printf"

thebigsmileXD

Posted 2017-10-04T16:19:46.850

Reputation: 21

another role play thing. Role play is useful when teaching methods. – Jay – 2017-10-05T01:56:04.533

2

Same problem here for some time as students were taught in earlier classes to write algorithms as input → compute → print.

I teach in Python so using the console I can refrain completely from using print, but found that I had to explicitly disrupt the bad pattern.

Students do not need to fully understand the notion of value returning in the first course (the third one is sufficient), what they need to understand is that printing is more restrictive than returning. This is easy to explain that one can always decide to print a value one got from a return but unprinting is not possible.

So when writing code that you want to be able to reuse, you return.

If they can manage a little arithmetic, the following exercise is nice :

  1. Write a naive function that tells if an integer is prime.
  2. Write a function that tells if an integer n is pseudo-prime (defined as : for all a < n, n divides a^n - a).
  3. Find the smallest integer that is pseudo-prime but not prime (561).

This will be unsufferable if the first two functions print instead of returning.

ysalmon

Posted 2017-10-04T16:19:46.850

Reputation: 201

Is there a logic error in paragraph 2? (a negation/ non-negation) – ctrl-alt-delor – 2017-10-05T08:09:49.040

I do not think so. I am defining n(>=2) to be pseudo-prime iff for all 2<=a<n, n divides (a^n-a). Is this weird ? Other notions of pseudo-primality exist but this one is nice for the example. – ysalmon – 2017-10-05T19:51:33.690

a paragraph is not a bullet point. “I teach in Python, so using …” – ctrl-alt-delor – 2017-10-06T07:40:07.077

2

Try to keep it simple.

printf() is a function for humans. Anything which printf() outputs is ignored by the program.

return() is a function (a control instruction) for the program. Anything it does is not (directly) observable by the human.

Of course, everyone here knows about side-effects and that printf() actually returns something, but I guess we don't shove the sad students into such areas...yet. However, it would be interesting to check what happens when you start explaining scanf() by starting with buffer overflow exploits and machine code...

Klaws

Posted 2017-10-04T16:19:46.850

Reputation: 121

scanf...that will drive those who cannot even get printf mad. – Jay – 2017-10-05T11:38:57.050

2

Based on your question, I infer that you think your students are "not getting it" and need further explanation.

However, when I look at the students' response, I suspect that they are getting it, but you simply have not presented them with a scenario where the two are not functionally equivalent.

Present them with a problem that cannot be solved without returning values.*

For the square() example, you could use the Pythagorean theorem (how to calculate C from A and B), or you could e.g. ask them to calculate the square of a square using only a method that squares a number (and does not immediately calculate the subsequent square).


*If you claim that there are cases where printing values is simply not the same as returning them, then the onus is on you to show an example to back up your claim.

I like the way your students approach the things you're trying to teach them. Instead of blindly accepting what you say, they are testing your claims by trying to do it their way. Prove them wrong.
This is annoying for you now, but that sort of behavior will do wonders for them if they ever start coding independently.


double square(double x)
{
  double sq = x*x;
  printf("the square of x is %lf\n", sq);
}

If your students think that this is okay (I'm aware it's not their own code, but your example), then they are thinking in terms of a single method call being able to solve their problem.

In an application that takes a number, squares it, and returns the result, it's indeed functionally equivalent (though obviously bad practice, but they don't see that yet).

However, how would they write the method if the application is supposed to apply the Pythagorean theorem? (i.e. calculating C based on A and B) This requires multiple squares to be calculated.

(Obviously, you have to enforce the use of a square() method, or otherwise they would write the formula in a single line, defeating the purpose of having to take multiple steps)

Without returning values, it would become impossible to calculate the value of square(A) + square(B), since the only thing the application ever does is display the square of a number. Without a return value, the application will be incapable of actually using the number for further calculations.

Flater

Posted 2017-10-04T16:19:46.850

Reputation: 510

Review: The 1st section is just more of the same (see other answers). I nearly got bored and stopped reading. However the 2nd section onward has value. – ctrl-alt-delor – 2017-10-05T14:50:13.563

1@ctrl-alt-delor: Swapped the sections around to improve the answer :) – Flater – 2017-10-05T14:53:40.070

1

Many helpful suggestions all around, from each of the different answers. In reading all of them, I was inspired to follow along with the advice of role-playing in a specific way that I thought up, which I will try to describe in this answer:

First, I need to choose a piece of example code, say the vector norm one in the question or a different one, but suppose it's the vector norm for the following exposition.

I will get students to volunteer for acting out the different functions: main(), square(), add(), sqrt(), norm(), printf() and (although this is somewhat conceptually wrong, but I believe will be helpful) return. Code will be provided in two styles for each of the computational functions (square(), add(), sqrt() and norm()): the correct way using return and the wrong way using printf().

Passing of parameters will happen by writing values to a piece of paper. Students should write the required values and keep them hidden. Whenever a function call takes place, the student acting out that function will only go ahead with the computation once he gets out the required pieces of paper corresponding to the parameters passed to it.

The key point I am considering to make is to distinguish between the insides of the computer (corresponding e.g. to the CPU) and what's visible by the outside user (e.g. the screen). Thus, I will erect a physical barrier (possibly using e.g. a wall of students) between the inside and the outside of the computer.

When acting printf() out, I will request the corresponding student to breach the barrier between the inside of the computer and the outside, stepping to the other side of the wall of students, and then showing the piece of paper for everyone to see (as if the rest of the class represented the user of the program). However, the students on the inside of the barrier (representing functions executing on the CPU) will be unable to see the piece of paper shown by the printf()-acting student. Also, I will request that the printf()-acting student hide each piece of paper after showing them to the rest of the class. Hopefully this will make it clear to them that after printing something with printf(), you're starving the functions that come after it of the required data to continue the computation.

On the other hand, the student acting out return will be asked to deliver the piece of paper to the student acting out the calling function, but not show it to the rest of the class (i.e. not breaching the barrier between the inside and outside of the computer).

Hopefully, after acting out the two versions of the code (using printf() and using return) in this way, it will become clear to the students exactly what happens in each situation. After trying this out and giving some time to see if the students indeed grasped the importance of return vs. printf(), I will report back here on whether the idea worked. Also, I appreciate any comments on ways to improve this specific strategy that I have described.

swineone

Posted 2017-10-04T16:19:46.850

Reputation: 241

1

I think it's overkill. I think it would be faster and more effective to thoroughly clear the words involved. For example, actually thoroughly clearing the words "return" and "function" and "operator," rather than taking them for granted as understood. To the extent that your approach works, I think it will be because it conveys the meanings of these words—but I think that by explicitly addressing the definitions, it could be done faster and more directly.

– Wildcard – 2017-10-05T00:13:24.420

@Wildcard I prefer 'define' or 'disambiguate', not another made up use of an existing word (with all the baggage that comes with cliques). – Sean Houlihane – 2017-10-05T11:55:31.717

@SeanHoulihane I can agree with that, but I do not simply mean "define" or "disambiguate." Those words do not convey the depth of understanding I refer to (nor the precision of steps involved in attaining it). Clearing a word is a precise action; much as the phrase "worst-case time complexity" should not be equated to "how long it takes to run." I can attest to the effectiveness of word clearing as described on the website I linked; merely disambiguating terms will not achieve the same result. – Wildcard – 2017-10-07T06:42:36.610

1

I don't know your concept of teaching and chronical order of the topics.

What i really love to teach, is the use of the debugger. Show them what is happening in Memory (Stack, etc). If you're using MS Visual Studio or similar tools you can walk through your program by watching your variables and memory.

In an easy understanding (very rudimentary) return handles the passing of a value from a function back to the caller or moving content of one memory address to another.

On the opposite printf shows formatted memory content and/or text to the user on the command line (stdout).

Just for fun and teaching purposes your student can write their own printf function. Not as a varidic one(*), but rather a simplified one that just puts out characters to stdout.

(*) although this could be fun too later on, but including the teaching why these function are risky too in use (memory leaks).

chris.stckhmr

Posted 2017-10-04T16:19:46.850

Reputation: 41

The 2nd paragraph is not clear can you improve it. And welcome to our community. Hope you can continue to contribute. – ctrl-alt-delor – 2017-10-05T08:59:14.697

1

There is return value and there are side-effects. The function printf is pure side effect: it puts things to stdout. In general, side effects are things that happen which persist beyond the lifetime of the function. Examples of these include files that are modified or created, or elements of a web page that are modified [JavaScript]. To have a return value, you must have a return statement.

Even in that event, a function might not be a "pure" function. Pure functions must return the same value every time when passed the same parameters. An example that does not meet this criterion: a random number generator.

ncmathsadist

Posted 2017-10-04T16:19:46.850

Reputation: 1 658

0

The way I learned about programming (Apple Basic, circa 1979) made it clear to me, so I would briefly explain this way:

Procedure - a clump of lines of code that "does something".
Function - an upgraded version: returns a value that can be placed in a variable.
Arguments - values passed in to the function.

Then I would draw a picture of the Stack on the board: a procedure has just a return location, the address where it was called. A function has a return value also, and can have arguments passed on the stack. (Local variables are on the stack too.)

This is a clear visual explanation that entirely explains how all computer programming actually works. And, as a bonus, you get the explanation of how function calls can be nested and return their values up the chain of calls. It is best to tell people the truth, as soon as they need to know it. This is the advice parents get for all situations with their children.

If the students still can't see why printing is different from returning a value, use an analogy: Pushing the Brake pedal in the car does two things: turns on the brake lights (printf), and actually does something - applying the brakes. If you only turn on the brake lights, you will warn the person behind you that you are about to helplessly smack in to something. They will apply their brakes, warning the person behind that... They should get the picture right away. Code has to actually do something. What? Manipulate values stored in memory. How? Show them what memory looks like, and the stack. QED

user3223

Posted 2017-10-04T16:19:46.850

Reputation: 1

1) printing is actually doing something, 2) "Show them what memory looks like, and the stack." sounds just confusing. – immibis – 2017-10-10T04:11:34.480

0

There's basically a single sentence to explain the problem here:

printf() shows you, the user, what's going on, but return shows the computer what's going on.

So how can you illustrate this?

First, I'd begin by having students write aboutprintf() and return and the difference between the two. At least try to get them to be able to say what the difference is.

Next, have students write a simple program without printf() and with return that works - maybe something like (I'm going to use python 3):

def add(a, b):
    return a + b

add(2, 5)

Have them explain why this doesn't return (heh, maybe don't use that term) any errors. Have them explain what return is doing in this case.

Then, another program:

def add(a, b):
    print(a+b)
    return(a+b)

add(2, 5)

Again, have them explain exactly what's going on.

Next:

def add(a, b):
    return(a+b)

print(add(2, 5))

Have them explain what's going on there.

Then move on to more "real" programs and see if this helps. The purpose is just to get them thinking about what they're doing and what the computer's doing. It sounds like something that can be remedied by just making them think.

heather

Posted 2017-10-04T16:19:46.850

Reputation: 3 623

0

I'm adding my answer to already huge list of answers, but I believe I'll add something novel, not listed above:

C language semantics is a mess. It is not your students' problem. C is mostly an imperative language, but because the authors didn't care to stick to one paradigm, they introduced a cross-breed of incompatible paradigms (mixing in some declarative style).

In a proper imperative languages like Forth the problem simply doesn't exist. Even remarkably poorly designed Basic and Matlab had solved this problem better: they use the same imperative style, and assign values to some variables. So that you are never tempted to write something like f(g(x)).

You want to solve this problem? Use a better, more consistent language. I understand the need to learn C, as it is an industry standard today, but it would be also wise to invest into the future by educating the students about why this language shouldn't be the industry standard.

wvxvw

Posted 2017-10-04T16:19:46.850

Reputation: 149