Introduction Video

Discussion Questions

  • What are some ways in which you can connect to the idea of functions and methods to ways of thinking (computational thinking), or ‘practices’ of computing?
  • How can you leverage the idea of functions and methods in block-based programming environments to black-box complex abstractions for students to use as ready-made functions for their projects? Try it out! Share examples.
  • Connect the ideas in this chapter to naive conceptions or areas of difficulty around functions shared in Chapter 14 (Naive Conceptions of Novice Programmers) and pedagogical ideas in Chapter 23.
    • Discuss: Which of these have you encountered in your teaching? Are there others? How do check if students are harboring these naive misconceptions? How do you address them? What pedagogies/strategies do you adopt?
    • Activity: Design a lesson plan that uses pedagogy ideas shared in chapters 7 and 23 to explicitly tackle a known misconception, and formatively probes and provides feedback on students’ understanding.The following some known naive conceptions are related to understanding of functions.
    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

Additional Examples

Baseball Statistics in Python: Refactoring & a math-based example

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.

 

Additional 'Fun with Language Games' exercises

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]

  1. “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:

 

‘Thube qubuubick brubown fubox jubumps ubovuber thube lubazy dubog.’

 

Can you write a translation function for “Ubbi dubbi”?

 

  1. 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:

 

‘Thebe qubuibick brobown fobox jubumps oboveber thebe labazy dobog.’

 

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?

 

  1. 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:

 

‘ToThohe qoquicockok bobrorowownon fofoxox jojumompopsos ovoveror tothohe lolazozyoy dodogog.’

 

Can you write a translation function for “robber-speak”? Can you perhaps reuse the “is_vowel” function we factored out earlier for this?

 

  1. 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:

 

‘WKH TXLFN EURZQ IRA MXPSV RYHU WKH ODCB GRJ.’

 

Can you write a function that encrypts messages using Caesar’s rule? How could you use that function to also decrypt it again?

 

Solutions to 'More Fun with Language Games' in Python

Here’s a function you might have written for the “ispeak” language:

def ispeak(text):
    result = ”
    for letter in text:
        if is_vowel(letter):
            result = result + ‘i’
        else:
            result = result + letter
    return result

And here’s a translation function for “Uasi”:

def uasi(text):
    result = ”
    for letter in text:
        if is_vowel(letter):
            idx = (‘aeiou’.find(letter.lower()) + 1) % 5
            subst = ‘aeiou'[idx]
            result = result + subst
        else:
            result = result + letter
    return result

You might also have written a function that translates a text to “l33t” like this:

def leet(text):
    result = ”
    for letter in text:
        if is_vowel(letter):
            subst = ‘4310µ'[‘aeiou’.find(letter)]
            result = result + subst
        else:
            result = result + letter
    return result

One for “Ubbi dubbi”:

def ubbidubbi(text):
    result = ”
    for letter in text:
        if is_vowel(letter):
            result = result + ‘ub’ + letter
        else:
            result = result + letter
    return result

And another one for the “b-language:

def bspeak(text):
    result = ”
    for letter in text:
        if is_vowel(letter):
            result = result + letter + ‘b’ + letter
        else:
            result = result + letter
    return result

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:

def robber(text):
    result = ”
    for letter in text:
        if is_vowel(letter) or letter in ‘ .’:
            result = result + letter
        else:
            result = result + letter + ‘o’ + letter
    return result

As well as a function that encrypts a message using Caesar’s cipher:

def caesar(text):
    result = ”
    for letter in text:
        if letter in ‘ .’:
            result = result + letter
        else:
            shifted = ord(letter.upper()) – 65 + 3
            result = result + chr((shifted % 26) + 65)
    return result

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:

def translate(text, rule):
    result = ”
    for letter in text:
        result = result + rule(letter)
    return result

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:

def ispeak(letter):
    if is_vowel(letter):
        return ‘i’
    return letter

or even more concisely by using an inline version of the “if” statement, called a “ternary operator”:

def ispeak(letter):
    return ‘i’ if is_vowel(letter) else letter

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:

translate(story, ispeak)

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?

>>> ispeak

We get a reference to the function itself:

<function ispeak at 0x10abc5268>

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:

def uasi(letter):
    if is_vowel(letter):
        return ‘aeiou'[(‘aeiou’.find(letter.lower()) + 1) % 5]
    return letter
def leet(letter):
    if is_vowel(letter):
        return ‘4310µ'[‘aeiou’.find(letter)]
    return letter
def ubbidubbi(letter):
    return ‘ub’ + letter if is_vowel(letter) else letter
def bspeak(letter):
    if is_vowel(letter):
        return letter + ‘b’ + letter
    return letter
def robber(letter):
    if is_vowel(letter) or letter in ‘ .’:
        return letter
    return letter + ‘o’ + letter
def caesar(letter):
    if letter in ‘ .’:
        return letter
    return chr(((ord(letter.upper()) – 65 + 3) % 26) + 65)

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:

for rule in [ispeak, uasi, leet, ubbidubbi, bspeak,
robber, caesar]:
print(translate(story, rule))

And get all of them instantly:

Thi qiick briwn fix jimps ivir thi lizy dig.
Thi qaock bruwn fux jamps uvir thi lezy dug.
Th3 qµ1ck br0wn f0x jµmps 0v3r th3 l4zy d0g.
Thube qubuubick brubown fubox jubumps ubovuber thube lubazy dubog.
Thebe qubuibick brobown fobox jubumps oboveber thebe labazy dobog.
ToThohe qoquicockok bobrorowownon fofoxox jojumompopsos ovoveror tothohe lolazozyoy dodogog.
WKH TXLFN EURZQ IRA MXPSV RYHU WKH ODCB GRJ.

Additional Resources

PyTamaro – Learning Problem Decomposition Visually

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.