9
$\begingroup$

I have a group of students with a very shaky understanding of functions and their purpose (encapsulation, reuse, modularization). The assignments I give them require them to write simple functions that return boolean T/F, an integer or a simple Python object (str, list, etc.), then have the caller use the returned value in a print statement. Their programs are written in Python and run from the Windows command line. The problem is that from the command line, the programs work but if you look inside it's a mess. The functions ignore the parameters to reach into the main for global variables (misunderstand the idea of parameter passing), the input validity checking is done in the caller (defeating the idea of encapsulation, e.g is day of month valid for a given month), or the function prints the result without returning anything. The worst that I get is the function does all the work of the main and of the function, for example when asking for a simple function to add 1 to its argument, I get:

def add1(x):
    myval = int(input("Number?"))
    print(myval+1)

add1(x)

...which of course is nonsense.

Here's my question... Can I craft an exercise that calls a simple function like add1() that would give a dramatically wrong output, e.g. cause the screen to "blow" up if the function printed instead of returned, or stalled if it kept asking for input. I'm thinking of something that reads a large file of text or numbers.

The students are at the community college level and are not programmers -- they are in electronics technology (and some are convinced that there is no software involved in electronics). I use Python but this is (mostly) language independent (I see the same shaky understanding with C++ functions in the Arduino world in another course).

Thanks in advance --Louis

$\endgroup$
4
  • $\begingroup$ Something that reads from stdin and writes to stdout (I am hopelessly dating myself here) is a good exercise. Next thing you know, they'll be dealing with stream processing of big data. $\endgroup$ – Scott Rowe Oct 28 '18 at 1:10
  • $\begingroup$ @ScottRowe Hmm, yes. In fact they will eventually be dealing with IoT concepts and aggregating data from multiple sensor "things". $\endgroup$ – Louis B. Oct 28 '18 at 19:11
  • $\begingroup$ And you didn't even need to throw "the book" (Structure and Interpretation of Computer Programs) at them! $\endgroup$ – Scott Rowe Oct 28 '18 at 21:57
  • $\begingroup$ If you want to make shell-script-y (a la @ScottRowe) PLUS functional code you could use/assune a tool like google-fire github.com/google/python-fire/blob/master/docs/guide.md . (not that I recommend it!! Just sayin'... ) $\endgroup$ – Rusi Oct 23 '20 at 4:31
6
$\begingroup$

Instead of an example, use a metaphor. Get yourself a frisbee - ultimate disk and two packs of sticky notes. You are the caller of a function. Another person is the function itself. Write a value on the sticky note and stick it to the frisbee. The pass it to the function (person). The person catches the disk, and replaces the stick note with a new one with a new computed function value. They then return the disk to you. You get the computed value and announce it.

Without the return value you get nothing. You can use any function you like but zero or one arguments is the easiest to manage.

A slightly more sophisticated version can be used to discuss pass by value and pass by reference.

Due to Mike Clancy of UCBerkeley. It is a classic from many years ago.

$\endgroup$
2
  • 2
    $\begingroup$ I've tried something like that but without the physical object. Scenario: we're both working late in the electronics lab and I forgot my calculator. I call out to you, Hey Fred, what's the square root of 4? You return the result of the computation, It's 2! I called the function by name, Fred, passed a value and waited for a returned value. The function didn't care what problem I was solving, and I the caller didn't care how the answer was computed. $\endgroup$ – Louis B. Oct 27 '18 at 1:13
  • $\begingroup$ That's a great suggestion @Buffy! Thanks for sharing that answer! $\endgroup$ – Onorio Catenacci Mar 18 '19 at 16:20
7
$\begingroup$

I saw the light at the end of the fiber optic cable as soon as you said that these are students of electronics. There is nothing more modular and encapsulated than that! Software is simply electronics without the hardware part.

If you said to them, "What are the building blocks of a simple AM radio receiver?" they would (hopefully) reply something like: antenna, tuning, rectification, filter, audio output... A crystal radio has all of those. They already know that it is pointless to build the antenna in to the filter, or duplicate the tuning parts more than once. They know that they can work in teams to produce parts that fit together to make a complete radio.

So speak to them in a language they already know: the functional relationship of parts and wholes. Using globals instead of parameters would be like having extra cables going outside the radio to connect different parts of it to the signal path or the power supply. Can you say "Rube Goldberg"? I knew you could.

Man, let me teach them. I have been studying electronics on my own since before anyone I knew even had seen a computer. Electronics students should be the easiest audience in the world to teach programming to. Use examples they already know.

$\endgroup$
2
  • $\begingroup$ I am sure to draw fire from the community by talking about Software in terms of real-world things, instead of explaining it as a closed, hermetic universe entirely consisting of concepts unrelated to anything the students have encountered before. $\endgroup$ – Scott Rowe Oct 27 '18 at 14:39
  • 1
    $\begingroup$ Bingo! And no, you won't get flak from me for mixing electronics and software. I deal in embedded systems and a system diagram can be any of pure hardware, pure software or a mix (e.g. state machine, FPGA). $\endgroup$ – Louis B. Oct 28 '18 at 18:59
3
$\begingroup$

Can I craft an exercise ... that would give a dramatically wrong output, e.g. cause the screen to "blow" up if the function print-ed instead of return-ed...

[my emphasis]

Early in my Python classes I do (something like) this:

>>> def foo(x):
...   print(2*x + 3)
... 
>>> def bar(x):
...   return (2*x + 3)
... 
>>> foo(4)
11
>>> bar(4)
11

So they look quite the same, don't they?

Now use them:

>>> [bar(x) for x in range(5)]
[3, 5, 7, 9, 11]
>>> [foo(x) for x in range(5)]
3
5
7
9
11
[None, None, None, None, None]
$\endgroup$
2
$\begingroup$

Sorry this is a half answer because I haven't concrete examples right now. Besides, the answer with the electronic analogies is already so great. But one point attracts me.

At the level where I imagine your students are, they do not need the modularity effectiveness that can be obtained with well structured functions. Indeed, you report from them mistakes of not following "your" way of doing things, but no concrete reason is dictating that your way is better than, for example, the cited way of implementing add1, therefore they may be lost wondering what are you asking them.

The "problem", so to say, is that in their situation functions do not serve any purpose of encapsulation, reuse, modularization. More: these are most probably just empty words for them, they don't have any encapsulation need and are therefore unable to recognize this never encountered concept. So this is why the electronic analogy is great: it lets them connect with something they know.

But from the computing side of things there's more. It seems that you are letting your students somehow follow a path of prehistoric programmers discovering how to take advantage of that strange creature called processor armed only with a sharp stone.

We may think that all our reuse or modularization needs will be perfectly served by goto. One sunny morning a programmer wakes up in his cave and observes that some problems are very very similar to one another and can be solved by repeating the same basic instructions with slight differences. He takes his stones and starts forging his goto into something more elaborate: the point where you land after the jump doesn't start right away but inspects some mysterious values called parameters and only after that the work can begin, being different depending on the actual parameters seen.

If this is the learning path that the students are following then what is needed is a case where they must do some operations in differing incarnations: if they do not feel this need they won't understand what are you wanting from them. In your introducing examples nothing of this kind was needed: if I just need to add1 then read, +1, write, end of story.

One problem alone doesn't need functions. A class of similar problems does. Ultimately, a computing system is one calculating a function, i.e. solving a class of problems, represented as differing inputs.

Now, unfortunately I don't have such examples in mind right now, but I hope it may be easier to invent them by having a clearer picture of their nature.

$\endgroup$
2
$\begingroup$

I like to think about pure function as a transmutation of inputs. Just like in Alchemy you connect Fire and Water to get Steam. Fire and Water are inputs, Steam is output. Or treat function as an assembly machine -- it takes resources, and converts those into another resource.

After that try posing a question: given two integer numbers as an input, what kinds of transmutation about these two numbers can you think about?

So first idea should be:

  • add numbers to get a number
  • multiply numbers
  • substract numbers
  • divide numbers

And usually that's it, I mean, student fantasy often stops here. Now your turn to make "how did I not think about it?" in student's brain:

  • substract first number from second (change argument order)
  • modulo divide two numbers
  • integer part of division
  • exponentiate numbers
  • treat numbers as triangle sides and compute hypotenuse by Pythagorean rule
  • min or max of two

Make sure students understood what every of these functions had done to two numbers. Ask them "anything else?" and if they produce more math examples (combinations), this is good, but at some step stop them and show this:

  • drop second (i.e., function which takes two arguments, and returns only first)
  • drop first
  • return those two back as a 2-tuple
  • return those two back as a swapped 2-tupple
  • return nothing

Now again, ask students "anything else?", make sure they understand how a function can take two arguments and do nothing, and return nothing, this is quite counterfactual for some. It is good if some list results are guessed, otherwise, show yourself:

  • return list, where first arg is repeated second arg times
  • range of numbers from min arg to max arg
  • sum of consecutive numbers from min arg to max arg
  • numbers from 1 to first arg with second arg step
  • numbers from 1 with first arg as step and second arg item count
  • first arg count primes, each divided by second arg

Make sure students are able to visualize each example, of how two numbers transform to list. The biggest sign of this is when they get the pattern and try to produce own examples. After some time, you break their brain again:

  • a list where number 42 is repeated first arg times, and then second arg is repeated 200 times
  • a list where first arg is repeated 3 times, then second arg is repeated 3 times, then first arg is repeated 4 times
  • a list where 3 is repeated first arg times, then 0 and this whole pattern is repeated second arg times x5

Ask students "anything else?", and if they grok pattern they should start some random list generations with some random numbers. The more random examples, the better, creativity is not easy. But at some point you break their brain once more:

  • given two args, return a function which will take one more arg and return sum of three args
  • given two args, return a function which will take one arg and ignore previous two args alltogether, and return square of an argument
  • given two args, return a function which takes list as an argument, and returns range of numbers from this list using two previous args

It is mind-shifting to think about function which returns a function, but if students can provide own examples (however fantastic), this is very good sign. Good if somebody can create function which returns a function which returns a function. But if not:

  • takes 2 args, returns a function, which takes no args and returns a function which takes 1 arg and returns pair of previous 2 args each multiplied by arg in innermost function (sic!)
  • return a function, which takes a function as an argument, and returns application of this arg function to previous 2 args
  • return a function, which takes a function as an argument, applies it first arg, and then applies itself to this result, and continues this process second arg times

After students brain is completely devastated by trying to imagine this, you show a few more examples:

  • compare two numbers and return "less", "greater", "equal"
  • return True if both are greater than 42, False otherwise
  • convert numbers to strings and return them combined
  • translate these numbers to Mongolian and return a tuple of string representations
  • ... show creativity!

Ok, and after all this done, ask the original question again "what can a function do with two numbers" and check student recall, how many of these examples were really grokked. The simple def plus(a, b): return a + b will now look different in student's head

P.S. After lesson is done, you tell "all the examples we talked about were about pure functions. But if we consider impure functions, then ..." :)

$\endgroup$
1
$\begingroup$

I think the example you showed on the original question is more about exception handling and unit testing rather than explaining the concept of function.

You may want to teach students on explaining the concept of function in a very simple way such as "Why is a lego so successful? Because it uses blocks! Function is like a lego block. It's about reusability, modular programming.", and give them enough time (maybe several weeks or so) to practice with different kind of examples to naturally get familiar with using functions.

The error-handling and unit-testing are more advanced concept and technique. It's probably best to teach these concepts once students are pretty good at writing code in general. I would suggest to actually teach this almost at the end of the course. It's important in the real-world software development, but for learning, I think these are less important compared to other topics.

$\endgroup$
0
$\begingroup$

You can call the function using the python unit test framework and run different tests against it. Simple print-statements and other things will most probably no longer work.

$\endgroup$
1
  • $\begingroup$ Exactly, I use pytest though, it's simpler. pytest errors out if there is unexpected output when importing the module. The problem is not in detecting their mistakes, those are pretty obvious, the real problem is that they persistently don't get it! $\endgroup$ – Louis B. Oct 27 '18 at 1:18
0
$\begingroup$

If classes are guns, functions are sort of like bullets. Not a guns take the same rounds, not all classes have the same functions. But some classes can have the same base classes therefore have some similar functions; some guns have the same manufacturer and take the same bullets. Instances of a class can call functions, just as a gun can shoot bullets. Maybe thats why call of duty calls it "create a class"...

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.