Session 10

Breaking Problems Down,
Building Programs Up

CS 1510
Introduction to Computing

Opening Exercise: An Infinite Series

Using calculus, we can show that this expression converges to a specific value:

    1/1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 + 1/13 - ...

What is the value?

Write a Python program that computes the sum for n terms, whatever n is.

Lessons from the Triangles

Task 1 from Lab 5 was pretty straightforward. I had something like this program in mind:

    for i in range(height):
        result=' '
        for j in range(i+1):
            result += '* '

But I was not clear enough in requiring you to use the expression print('*', end=' '), so many of you wrote a simpler solution doing string arithmetic, such as:

    result = ''
    for i in range(0, height):
        result += '* '

Like the shorter solution many of you found to our opening exercise last time, this loop takes advantage of the relationship between successive rows of stars. That is a nifty bit of problem solving! And it worked fine, until you came to Task 2:

            * *
          * * *
        * * * *
      * * * * *
    * * * * * *

We still have the same relationship between lines of stars, we aren't adding new ones on the right; we are adding them on the left. And that means we are taking away a space (and its separator space), too. That's hard to do in a single line of string manipulation. (note).

Rather than be stopped by a brick wall, what can we do? Your textbook gives some helpful advice, including Stop and think and Understand, then visualize. Thinking in terms of pictures and images can often help us arrive at a new solution.

          * * *
    - - - . . .

        * * * *
    - - . . . .

      * * * * *
    - . . . . .

We are printing longer lines of stars and shorter lines of spaces. How does that match our program? We go from this in Task 1:

    for each line
        print stars

... to this in Task 2:

    for each line
        print spaces
        print stars

This isn't Python. It's an algorithm for solving the problem in any language. It doesn't have a lot of detail, but most everyone can understand what it means.

We can refine our algorithm by adding more details. In particular, how many spaces and how many stars? That depends on which line we are on...

    for each line i
        number of stars = i + 1
        number of spaces = height - number of stars
        print that many spaces
        print that many stars

We can make this shorter by beginning to use ideas from our programming language, such as variables.

    for each line i
        n = i + 1                # number of stars
        s = height - n           # number of spaces
        print s spaces
        print n stars

Now, we are ready to write code. We have several ways of printing s spaces and printing n stars: a loop with print('*', end=' '), a loop that accumulates a string, or string arithmetic. At one level, it doesn't really matter. They all implement the same algorithm.

This is the sort of thinking that lead me to write this solution:

    for line in range(height):
        stars = line + 1
        for i in range(height-stars):
            print(' ', end=' ')
        for i in range(stars):
           print('*', end=' ')

This is a bit longer than some solutions, but keep in mind that the assignment asked you to use print('*', end=' '), not string arithmetic.

For any given algorithm, there are often many different ways to write a program that implements the algorithm. Try to write programs using language features that you understand, and try to understand the language features we study in class.

For any given problem, there are often many different algorithms for solving the problem. Try to think about challenging problems in different ways, and break big or scary steps down into parts.

Quick exercise: Can we think of our right-justified triangle in terms of "shifting" stars in from the right? How about this?

A closing note... Before we move on, how might we perform Task 2 in the simpler way that many of you tried? Here is how I might implement your idea:

    result = ''                  # an empty string of stars
    spaces = '  ' * height       # a full string of spaces
    for i in range(0, height):
        spaces = spaces[0:(len(spaces)-2)]
        result += '* '
        print(spaces + result)

Trace this code for a small value of height, such as 4. Draw pictures. Watch how the values of spaces and result change on each pass: minus two spaces, plus two stars.

Lessons from the Strings

The goal of Task 3 was to give you some practice with accessing the parts of a string. Two big lessons are:

Once you understood how the loop variable in for ch in string: worked, many of you found Task 4 to be the easiest task of the day. I hope that was a good way to end. Just keep in mind: = and == mean different things!

Growing a Program

Homework 4 asked you to extend an existing program with three features:

Categorizing a passing efficiency required a range test of the sort we have used before. Nothing new there.

Making sure that the number of passes attempted is at least as big as the number of passes completed requires a loop, because the user could make the same mistake more than once. The loop is similar in spirit to the sentinel loop we studied last week, with an important difference. Instead of looking for a specific value that tells us to stop, we are looking a range of legal values. In a sense, the loop condition is inverted: we are looking for bad values that make us keep going.

We have seen two ways to implement such loops. The first tries, and then loops on a bad value:

    passes_attempted_in = input("Passes attempted: ")
    passes_attempted    = int(passes_attempted_in)
    while passes_attempted < passes_completed:
        print("The number of attempts must be >= number of completions.")
        passes_attempted_in = input("Passes attempted: ")
        passes_attempted = int(passes_attempted_in)

The second uses a loop and a half and breaks out on a good value:

    while True:
        passes_attempted_in = input("Passes attempted: ")
        passes_attempted    = int(passes_attempted_in)
        if passes_attempted >= passes_completed:
        print("The number of attempts must be >= number of completions.")

Either works fine. The loop and a half is a bit cleaner, but separates the error message from the next attempt.

Allowing the user to have multiple interactions -- computing the passing efficiency of multiple players on the same run of the program -- also requires a loop. This time, it is a good old sentinel loop. When I wrote my solution, I used the loop and a half technique:

    while True:
        passes_completed_in = input("Passes completed: ")
        passes_completed    = int(passes_completed_in)

        if passes_completed == -1:

        # ... read the remaining inputs
        # ... compute the rating
        # ... classify the rating
        # ... print the results

The hardest part of this task is indenting what used to be the entire program. Thank goodness for IDLE's Indent Region command!

This program is quote long... I can't "grok" it in a single glance... Decompose it into parts -- later... For now, some comments can help us navigate the code.

Do you have any other questions?

Wrap Up

Eugene Wallingford ..... ..... September 25, 2014