Code in a program (including functions) are executed in the order they appear in the program text (related to concept of ‘Sequence’) |
The meanings of identifiers affect what the system does; for example a variable called “longest” will contain the longest string |
Function arguments must be literal or variable names |
Argument expression are passed unevaluated |
Parameter-passing forms links between the calling-code and the function |
The caller must always store the return value in a variable |
Return values always get stored for the caller to access |
Returning a value is similar to printing it. You can do it after you’ve called the function |
Incorrect order of function parameters |
A function must always return the value of a (local) variable |
The Python cat drawing example in the chapter only required basic string concatenation. Here is one that will require some mathematical understanding as well. Like the last example it encourages modularity, but it also puts more importance on abstraction and reuse. By going beyond strings and printing this example also emphasizes the difference between returning values and printing output. Finally, it can be used to introduce the concept of refactoring.
This example uses baseball statistics but it is important to know your audience. Not all students are interested in baseball/sports. You know your students best and you can chose another domain that they will find more engaging.
Baseball has long been ruled by statistics and given basic information, students can write functions to calculate traditional statistics such as batting average:
def batting_average(singles,doubles,triples,homeruns,at_bats):
return (singles+doubles+triples+homeruns)/at_bats
A more interesting statistic is on base percentage. You calculate that by adding up all hits, walks, and the number of times the player is hit by a pitch, that is, all the ways a player can reach base, and divide by the number of at bats:
def calculate_OBP(singles,doubles,triples,homeruns,hits, walks hit_by_pitch, at_bats):
return (singles+doubles+triples+homeruns+walks+hit_by_pitch)/at_bats
Students will notice that both functions require adding up singles, doubles, triples, and homeruns. We could improve our program by pulling out or “refactoring” the code shared by both functions into a new function:
def calc_hits(singles,doubles,triples,homeruns):
return singles+doubles+triples+homeruns
which now lets us simplify our two other functions:
def batting_average(singles,doubles,triples,homeruns,at_bats):
hits = calc_hits(singles,doubles,triples,homeruns)
return hits/at_bats
def calculate_OBP(singles,doubles,triples,homeruns,hits, walks, hit_by_pitch, at_bats):
hits = calc_hits(singles,doubles,triples,homeruns)
return (hits+walks+hit_by_pitch)/at_bats
If your students have studied algebra, you can relate refactoring a program to factoring in math. In algebra, when you have an expression such as 5x^2 + 10x, you can factor, or pull out an x to simplify the expression to x(5x+10). Refactoring is very similar. We notice some
common piece of functionality and “pull it out” into its own function.
By abstracting out a bit of functionality and giving it a name we increase our programs readability and also make it less error prone since we’re only performing common operations in one location. We can then present additional modern baseball statistics such as slugging percentage, calculated as follows:
def calc_slugging(singles,doubles,triples,hrs,at_bats):
return (singles + 2*doubles + 3*triples + 4*hrs)/at_bats
The hope is that students will realize that even though slugging percentage uses singles, doubles, triples, and homeruns, they can’t used the refactored calc_hits routine.
Finally, we can look at OPS which is calculated by adding together slugging percentage and on base percentage. By this point, students should be comfortable using the other statistical functions they’ve built in calculating OPS but there should be some debate as to how to
structure a program that implements all of this.
Should they write calc_OPS so that it has a very long list of parameters:
def calc_OPS(singles,doubles,triples,hrs,hits,walks,hpb,at_bats):
obp = calc_OBP(singles,doubles,triples,homeruns,hits,walks,hbp,at_bats)
slugging = calc_slugging(singles,doubles,triples,homeruns,at_bats)
return obp+slugging
or should we calculate obp and slugging first:
obp = calc_OBP(singles,doubles,triples,homeruns,hits,walks,hbp,at_bats)
slugging = calc_slugging(singles,doubles,triples,homeruns,at_bats)
def calc_OPS(obp,slugging):
return obp+slugging
There is no single right answer to this but it is important that students discuss the possibilities and tradeoffs.
In the Languages Games with Functions and More Fun With Languages we explored developing translation functions for several “play languages”. We presented you with some challenges to try out on your own. Here are a few more from Jens Mönig 🙂
[Solutions to all are provided in the next section]
“Ubbi dubbi” https://en.wikipedia.org/wiki/Ubbi_dubbi is another play language that is especially fun to pronounce. It inserts the syllable “ub” before every vowel:
Can you write a translation function for “Ubbi dubbi”?
In many countries there are play languages that double every vowel around a particular consonant: In Germany, Latin America, Somalia, and Japan kids speak the “b-language”: It inserts the letter “b” after every vowel and then repeats the vowel:
Can you write a translation function for the “b-language”?
Oh, and in other countries, like Spain, Albania and Malaysia kids use “f” instead of “b”. in Sweden, Denmark, Latvia, Macedonia, Romania, Slovenia and Argentina the “p-language” is popular. And there’s more: The “s-language” is spoken by kids in Russia and Korea, the “g-language” in Turkey and Indonesia, the “k-language” in Greece, the “v-language” in France and Egypt, and the “z-language” in Persian speaking countries (https://en.wikipedia.org/wiki/Language_game). Can you write translation functions for each of these languages? Can you perhaps even write one function that serves all of these languages?
In Scandinavia kids love to speak in a secret “robber” language called “Røversprog” (Norway) or “Goggamál” (Iceland). It works like this: Instead of checking for vowels like we did until now, the “robber language” inserts the letter “o” after every consonant and then repeats the consonant. The result then looks like this:
Can you write a translation function for “robber-speak”? Can you perhaps reuse the “is_vowel” function we factored out earlier for this?
Roman general Julius Caesar also used a secret language (https://en.wikipedia.org/wiki/Caesar_cipher), not to speak it but to garble written messages so enemies intercepting them could not make sense of them. His encryption was similar to the “Uasi” play language, except that he shifted every letter in the alphabet, not just vowels, and he shifted them by 3 positions. Since the Romans only used 26 capital letters, our story encrypted using the Caesar cipher looks like this:
Can you write a function that encrypts messages using Caesar’s rule? How could you use that function to also decrypt it again?
Here’s a function you might have written for the “ispeak” language:
And here’s a translation function for “Uasi”:
You might also have written a function that translates a text to “l33t” like this:
One for “Ubbi dubbi”:
And another one for the “b-language:
If you did all the exercises, you might even have come up with a slightly more complicated function yet for the Scandinavian “robber-speak” language:
As well as a function that encrypts a message using Caesar’s cipher:
By now surely you’ve noticed that all these functions look very similar, even though they encode distinctly different rules that cannot be covered by simply adding another parameter. Whenever we find ourselves writing almost the same function over and over again it’s a good idea to stop and think about how we can formulate the idea in a more general way. What all of these functions have in common is that they take each letter of an input and according to a rule that’s different for each language either replace it for another text or leave it as is. If we think about the “rule” as a function that takes a letter as its input and returns whatever its rule prescribes, we can formulate a general translation function that does all the rest:
Notice the parentheses following the word “rule”? We didn’t define any function named “rule”, so how can we call it inside our “translate” function? Remember that “rule” is the name of an input for “translate”, a formal parameter. The actual value will be a function, not its result. That function can be any function, and it will be assigned to the parameter – a variable – named “rule”. Adding parentheses will then evaluate whichever function we give it.
Now we can reformulate the individual language-functions so they don’t have to deal with taking a text apart and putting it back together again. Instead we can write them in such a way that they can decide what to do to an individual character. Here’s how we can write this for the “ispeak” language:
or even more concisely by using an inline version of the “if” statement, called a “ternary operator”:
We can translate our story to “ispeak” by calling the “translate” function on the story and by passing in the translation rule as another input:
Notice that the second input named “ispeak” is not written with trailing parentheses, even though it is a function that we’ve already defined. Remember what happens when we “forget” to add parentheses when entering a function at the command prompt?
We get a reference to the function itself:
This lets us call our “translate” function and let it operate on any rule we can formulate as a function!
The translation rules for our other six play-languages can also each be written much more concise:
And now, finally, we can write a simple program that displays our story in all the seven play-languages we’ve defined in a single function call:
And get all of them instantly:
PyTamaro is a Python graphics library with a twist. Plus it is a web site with a collection of short, engaging, visual, and theoretically well-founded programming activities. While it’s already being used by teachers in Switzerland, it still is under development.
It is a wonderful resource for Python programming courses, developed for high school computer science teaching in Switzerland by Dr. Matthias Hauswirth’s Lugano Computing Education Research Lab.