CS 261 Lab #7

March 7th

Goals:

This week you'll get some hands-on practice with recursion. The last problem's challenging, but see if you can't make some progress on it if you get that far.

Partners

In each lab this semester you will work with a randomly assigned partner. (I'll have Zoom randomly set up breakout rooms.) Please be kind in your interactions with your partners! Keep in mind that students in this class have a range of previous programming experience, and that some have been college students for longer than others. We're all in this together, and you have something to learn from your partner, no matter who they are or what their previous experiences have been. I expect that group members will collaborate and work together on each step of the lab.

Cursing and Recursing

  1. Take a moment to introduce yourself to your partner(s). After social pleasantries are complete, pick one member of the team to be the "typer". They'll share their screen while editing the lab code in BlueJ. (I think BlueJ works better for these interactions than Eclipse. It's easier to see when sharing screens, and makes it easier to quickly test individual methods than Eclipse does.) Group members should contribute equally while working through the problems below and discuss all code to be written, though only the "typer" will be able to edit code. Resist the temptation to have both members work simultaneously in BlueJ — you're much more likely to "drift apart" over the course of the lab if you do so. The goal here is to have a partner who's engaged on exactly the same step of the lab as you are.
  2. Start by downloading the lab project, extract its contents, and open it in BlueJ. The RecursiveCode class contains static recursive methods — some that are already working, and some that you'll finish implementing.

  3. The main method creates a list and passes it to largest, a recursive method that finds the largest value in the list. I've added some print statements to help illustrate how the recursion works. Run the main method and study the output until you understand what the output implies about how the method runs. In particular, note how calls to largest get "put on hold" when making recursive calls to largest, and picked up again after the recursive calls return. I would suggest drawing a diagram on paper, where you explicitly show when one method calls another, what gets passed, and what each method invocation has as local variables. Pay particular attention to how the list changes from call to call. (Remember that when we pass lists to methods we're really passing an object reference to those lists. Each recursive call is really sharing the same list, rather than making copies of it.) There's no need to demonstrate anything as part of this problem, but you'll be in better shape for the rest of the lab if you don't proceed until enlightenment occurs.
  4. As noted, largest destroys the list as it works. At each recursive step it makes the list shorter, until it runs out. Only the final item is left, since the base case doesn't remove that last item. Uncomment the nums.add line and test the method again. Now the list gets rebuilt as we come back out of the recursion! Think through what's being added to what, and why that one line now magically causes the list to be preserved. You might again consider drawing a diagram to help...
  5. Now that you're up to speed on the details again, practice your recursive ninja skills by finishing the definition of printArray. It should print the contents of the array, recursively, with one value per line. If you uncomment the printArray call in main and the line above it, after finishing your method you should see the following output:

    2
    6
    3
    5
    4
    9
    7
    
  6. Now edit your printArray method so that it prints the array contents in reverse order. This is easier than it seems. (Should you print and then recurse, or recurse and then print?) If you find yourself writing a version that decrements the index at each call, you've made it too hard...

    7
    9
    4
    5
    3
    6
    2
    
  7. The initial version of our largest method destroyed the list as it went. Instead of destroying (and, eventually, rebuilding) the list, we could have used an iterator to work through it instead. Finish the recursive method countEvens that works its way through a list (or any other iterable collection of integers) via iterator rather than by changing the list. It should return the number of even values in the list. (You can use the % operator to help you find evens.) The comments above the method remind you that you can use hasNext and next on an iterator, and there's test code in main already that creates an iterator and passes it to the method. This one's a little more interesting because there are three cases instead of just two: There's a base case, and in the recursive case you'll have two cases — one for even values and one for odd.
  8. Next, let's write one that builds and returns a list: Finish the definition of returnNegativeNums. It should take an iterator to a collection of Integers as in the previous method, but the method should return a list containing all of the negative integers found in the input collection. The basic outline will look a lot like evens from class. Hint: In the base case, create a new list that will be used to hold the negative values. In the recursive case you'll get a list back from the recursive call to returnNegativeNums, and if you've found a new negative value you can add it to the list before returning it.
  9. For your last bit of wizardry, see if you can make some progress on the definition of powerSet. It should take a string as input and generate all variations on that string by length. For example, "cat" would produce a list containing ["cat", "at", "ct", "ca", "c", "a", "t", ""]. (The order of the strings within the list doesn't matter as long as they're all there.) Think carefully about how a recursive call could help you. For example, what would powerSet return for "at"? How could you patch that up to produce the full answer for "cat"?

Extras

If you've got extra time and are bored, consider trying the following:
  1. Instead of finding strings of varying length, as in powerSet, see if you can generate all permutations of a string. For "cat" you'd get ["cat", "cta", "act", "atc", "tca", "tac"] in some order.
  2. Think about how to generate all "words" that correspond to a given phone number. (Most keys on the phone keypad have three or four letters associated with them.)


Brad Richards, 2023