Education Technology

“Jingle Bells, Jingle Bells” … Falling Snow & Python Lists

Posted 12/09/2022 by John Hanna

Ready for winter? (Or maybe just the winter break from school?) How about a little karaoke during your snowstorm? The TI-Nspire™ Document contains a Python program sure to put you in that wintry mood because it:

  • Has falling snow.
  • Plays the notes to “Jingle Bells.”
  • Displays the synchronized karaoke-style crawl of the lyrics on the screen.

Note: See the file for authoring credits.

Python program to play “Jingle Bells” while snow is falling.

At the heart of the code is … a list:

Python code for“Jingle Bells.”
Python code for part of the song “Jingle Bells.”

Each element of the list myscore contains three pieces of information about each of the notes of the song: the note name, the note length and the lyric associated with that note (usually just a syllable in this case). Each element is a Python tuple such as (“f4”,0.125,“the”) which represents the note “F” in the 4th octave as a quarter note (0.25) for the word “the.”

The program also uses a Ball Class (see my previous post about Python classes) to represent the snowflakes and a list of flakes to maintain all the falling bits of snow. This blog dives a little deeper into the Python list data structure. Teach your students about lists, while bringing a snowstorm indoors.

Python list refresher

Lists are part of a larger family of Python data structures. Tuples, lists, sets and dictionaries are all similar data structures and each has its own set of special properties, syntax and punctuation. If you are familiar with lists on the TI-84 Plus CE Python or TI-Nspire™ CX II graphing calculator, you will find some interesting and distinctive features when using Python lists.

The list symbol is the pair of square brackets: [ ]. This symbol is used to enclose list elements like
myListA = [7, 15, 4]
     myListB = [ ] # an empty list
and to refer to an element of a list as in myListA[1], which is the number 15 seen above. List indices (subscripts) begin with 0.

Initializing a list

As seen in the above example, a list can be initialized by stating its elements inside the brackets and separated by commas.

We can use the list( ) method to create a list:
myListA = list( range(10) ) produces the list [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
chars = list( ‘Hello’ ) produces the list [ ‘H’, ‘e’, ‘l’, ‘l’, ‘o’ ] (A string is a Python iterable. An iterable is a Python object that can be used as a sequence.)

Here’s a unique Python feature:

myListC = [0] * 5

creates the list [0, 0, 0, 0, 0] and stores it in the variable myListC. This is very different than the comparable operation on both the TI-84 Plus CE Python or TI-Nspire™ CX II graphing calculators, which just multiplies each element of the list by the scalar value 5.

Then what does [1, 2, 3] * 5 create? Here’s the answer using a Shell:

Python shell showing what the code  [1, 2, 3] * 5 creates.

Are you surprised? Rather than producing [5, 10, 15] as on TI-84 Plus CE Python or TI-Nspire™ CX II graphing calculator, it instead makes five “copies” of the elements in the list. Also notice that the operation is commutative.

For an example of this type of list construction in action, see Unit 4 Application, which creates a simulation of tossing two dice and monitoring the different sums that are accumulated and analyzed.

Building a list using .append()

You have probably seen the list method a.append(x) to add one element x to the list a. A comparable technique is a += [x] (short for a = a + [x]) which “adds” the list containing x to the end of list a. The square brackets around x are required: you are adding a list (containing one or more elements) to the end of a list.

List comprehension

Another exciting feature of Python, list comprehension is useful for initializing a list containing different values. A traditional way of making a list with specific values is to use a for loop:
myListD = [0]*10 # create a list of 10 elements, all 0.
for i in range[10]:
myListD[i] = i

makes myListD contain [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Using list comprehension, we can write a one-line statement that performs the tasks of the three statements above:

Code for one-line statement that performs three statements mentioned earlier.

Other examples:
myListE = [ x**2 for x in range(1, 6)]
creates myListE = [1,4,9,16,25]

myListF = [ randint(1,25) for i in range(8) ]
creates a list of 8 random values, each between 1 and 25

A special warning!

In the TI-84 Plus CE Python or TI-Nspire™ CX II graphing calculator we can assign a list to another list:

TI-84 Plus CE list: L2→L3
TI-Nspire™ CX II list: L3:=L2

Each statement above stores a copy of the list L2 into the variable L3 so that, after execution, L2 and L3 are separate lists containing the same set of values.

But, in Python:
suppose a = [1, 2, 3]
then b = a does something completely different: rather than having two separate lists, there is only one list with two different names. Both the variable a and the variable b refer to the same list. Making a change to one of the lists makes the same change to the other because they are the same list. Try it yourself in a Python Shell:

Python Shell with code.

To make a copy of a list use the special list method .copy():
a = [0, 1, 2, 3, 4, 5, 6, 7, 8]
b = a.copy( )

Using the .copy() function creates two separate lists containing the same values. Here’s another Shell demo:

Another Python Shell example.

Example: Make a list of prime numbers

The following function returns a list of primes (primelist) up to the number n, but it also uses that list to find more primes inside the two loops. Elegant!

Python Shell with code using prime numbers.

Note: The Python for loop has an optional else: clause which is executed when the loop ends normally. The inner loop (for p in primelist): tests the current number i to see if it is divisible by any of the primes found so far. If so, the break is executed (the loop is terminated early) and the next value of i is processed. If the inner loop ends normally (that is, i is not divisible by any of the primes found so far), then the number i is added to the primelist in the else: block.

Back to jingle

You didn’t think I would wrap this up without giving you your own snowstorm? And I bet you are still singing “Jingle bells, jingle bells … jingle all the way. …”

The TI-Nspire™ Document works with or without a TI-Innovator™ Hub and can even be used with an external speaker attached to the TI-Innovator™ Hub if you have the right cable. Just use the appropriate statement in the play_note( ) function in the program:

Python code to play the notes.
Code for denoting a sound or speaker.

There is also a second #commented song included in the programs, “Auld Lang Syne.”

The flakes list in the program keeps track of the snowflakes on the screen. But the list does not get too large: when a flake hits the “ground,” it is removed from the list using the .remove() method discussed earlier. The flakes “float” to the ground by adding some randomness to each flake’s position:

Python code to show snow.
Code for Ball.move() method in

There are three special functions in the program:

Python code showing additional  notes.

This function handles the audio portion of the music. Note the if hub: statement. This allows the program to run even if there is no TI-Innovator™ Hub attached. Also, timing of the notes is handled with the time() function, not with a sleep() function. This is more accurate.

Python code to play sounds.

play_note( ) creates the sound on either the TI-Innovator™ Hub or its external speaker. Note that both sound.note() and spkr.note() are included here but only one should be active depending on your hardware setup.

Python code to show lyrics in a karaoke-style crawl.

This function is a little more complicated: It displays the karaoke crawl, highlighting the current lyric in red.

Notice that the snow accumulates on the ground. Eventually, the snowman will be “buried” as the snow gets deeper:

Final program running — it’s a blizzard!

So, if you are familiar with reading sheet music, your challenge is to create another song by building a similar list. Be sure to share your work with @TICalculators online.

Lists summary

  • Multiplying a list by a number “expands” the list by that factor.
  • List comprehension is a convenient way to build a list.
  • Be careful when trying to make a copy of a list. Use the .copy( ) method rather than a simple assignment statement.
  • Lists can store other complex data structures, not just numbers or strings.
  • Check out the other list methods found in the Python documentation.

Happy winter!

About the author: John Hanna is a retired teacher splitting time between sailing in New Jersey and mountain biking in Florida (did he get that backwards?), still getting kicks out of working with TI to provide feedback on new products, and developing meaningful programming content for mathematics and science ... and still having fun with all the graphing calculators and the accompanying toys.