In fall semesters I have a TA gig for the introduction to EE class at ISU, EE 185. In the labs we have to teach the freshmen how to program a computer in Matlab and a little bit of C. It's a fun job, but very challenging: most of the students are complete newbies who have trouble with the simplest of programming concepts, and we've got to do whatever we can to help them out.
One huge meta-problem that always gives them grief is debugging. When their programs don't work -- and they usually don't work at first -- they don't know how to go through the program and figure out why it doesn't work.
I came across a fascinating article recently by Chris Okasaki, talking about program testing helping students learn. The idea is that, when you give out a problem for students to do, you also give them a program to test their solution. They write a solution, and the test code you give them runs their program on 50 or so test cases and complains when the solution fails on some of the test cases. This gives instant feedback on their mistakes, so debugging is reduced to squashing a series of bugs. He's had good results with this in the classes he teaches, and the students in EE 185 tend to have a lot of glaring bugs because they only use one or two makeshift test cases for their programs.
So I'm thinking of doing a lesson where we explicitly walk them through common debugging techniques: printf debugging and stepping through your program with a debugger. Both of these are absolutely basic techniques that would solve about 30% of the headaches I see in that class. So, mostly for my benefit, here's a lesson plan that I'd like to use in some form next semester. The prerequisites for doing this lesson are loops, arrays, and functions. So let's start with a title:
Debugging and testing
When the programs you write don't work, you need to find out what you did wrong and fix it. This is called debugging. There are some ways of debugging your code that can make it a lot easier. Let's look an a bit of example code. It calculates the sum of the numbers from 1 to 10:
This code doesn't work. The result is supposed to be 1+2+3+4+5+6+7+8+9+10 = 55, but instead we get 45. How do we find the mistake?
Very simple debugging
The first method is to have your code print out the value of some variables while it's running. This can be as easy as taking away some semicolons:
This isn't very helpful so far, but it does show that the code seems to be stopping early. Let's try looking at the progress of another variable, by printing i each time through the loop:
Run this and look at the output. What is the problem? Fix the code and include it in your lab report.
Stepping through your code
Another good way to find mistakes is to go through your program, line by line, and see what it's doing. Figure out what all the variables will be, and check if this matches your idea if what should happen.
Matlab has a way of doing this for you, called stepping. There's a "Step" command in the Debug menu, which you can also use by pressing F10. Try it out: open an M-file and step through it. Each time you step, Matlab runs one line of code. If you look at the variables in your workspace, they will show the values of the variables right now. This can really help you understand what a program is doing.
Here is more information on debugging M-files.
The assignment, part 1:
Write a function called splitfirst that will take an array and split it around the first occurrence of a given element. For example, if your function is given the array [3,1,4,1,5,9] and it told to split it around 4, then the function should return this matrix:
> splitfirst([3,1,4,1,5,9], 4)
ans =
3 1 0
1 5 9
Notice that the extra space was filled with a 0. Here's another example. If the function is given the array [1,2,3,4,5,6,7,8] and told to split it around 6, then it should return this matrix:
> splitfirst([1,2,3,4,5,6,7,8], 6)
ans =
1 2 3 4 5
7 8 0 0 0
The 6 has been removed, and everything after it has been moved into the next row. One final requirement: no empty rows! If you try to split at the beginning or end of an array, this should not produce a result with a row of all zeros. Instead, it should do something like this:
> splitfirst([1,2,3,4], 1)
ans =
2 3 4
Focus on getting your code working at all, then worry about this.
Writing the code for this problem is a little tricky, so you've been given a function called "above" in above.m which will help. The above function will take one matrix and put it on top of another, filling the remaining spaces with 0. To produce the first answer, you could do this:
> above([3,1], [1,5,9])
ans =
3 1 0
1 5 9
And for the second answer, you could do this:
> above([1,2,3,4,5], [7,8])
ans =
1 2 3 4 5
7 8 0 0 0
Above also works with arrays that have more than one row. For example:
> above([1, 2; 3, 4], [5,6,7,8])
ans =
1 2 0 0
3 4 0 0
5 6 7 8
Empty rows will be removed, which should be helpful:
> above([], [1,2,3])
ans =
1 2 3
Look at splitfirst.m and add code to do the splitting. Good luck.
Important note: you can test your code with the "testsplitfirst" function. Check it out:
> testsplitfirst
Testing splitfirst...
Failed: splitfirst([1,2,3,4], 1) should be [2,3,4]
Failed: splitfirst([1,2,3,4], 4) should be [2,3,4]
Passed 4/6 tests.
Your goal is to get it to pass all the tests. Include your test results in your lab report.
The assignment, part 2:
If you got this far, then congratulations on completing part 1. Part 2 is to split an array by every occurrence of a given element, not just the first one. For example:
> splitevery([3,1,4,1,5,9], 1)
ans =
3 0
4 0
5 9
Here, every time a 1 appears in the sequence, everything following it is moved down another row. As before, extra space is filled with zeros. Here's another example:
> splitevery([1,2,3,4,5,6,7,8], 4)
ans =
1 2 3 0
5 6 7 8
Since the 4 only appears once, this has exactly the same effect as using splitfirst. Here's an even more extreme case:
> splitevery([1,2,3,4,5], 9)
ans = 1 2 3 4 5
Since 9 doesn't occur anywhere in the array, it doesn't get split.
As in part 1, there should be no rows filled with just zeros. Here's an example that illustrates what's supposed to happen:
> splitevery([5, 4, 3, 5, 5, 5, 5, 1, 2, 5], 5)
ans =
4 3
1 2
Look at the file splitevery.m and add code. You can test it with the "testsplitevery" function:
> testsplitevery
Testing splitevery...
Passed 4 out of 4 tests.
Include your test results in your lab report.
One huge meta-problem that always gives them grief is debugging. When their programs don't work -- and they usually don't work at first -- they don't know how to go through the program and figure out why it doesn't work.
I came across a fascinating article recently by Chris Okasaki, talking about program testing helping students learn. The idea is that, when you give out a problem for students to do, you also give them a program to test their solution. They write a solution, and the test code you give them runs their program on 50 or so test cases and complains when the solution fails on some of the test cases. This gives instant feedback on their mistakes, so debugging is reduced to squashing a series of bugs. He's had good results with this in the classes he teaches, and the students in EE 185 tend to have a lot of glaring bugs because they only use one or two makeshift test cases for their programs.
So I'm thinking of doing a lesson where we explicitly walk them through common debugging techniques: printf debugging and stepping through your program with a debugger. Both of these are absolutely basic techniques that would solve about 30% of the headaches I see in that class. So, mostly for my benefit, here's a lesson plan that I'd like to use in some form next semester. The prerequisites for doing this lesson are loops, arrays, and functions. So let's start with a title:
Debugging and testing
When the programs you write don't work, you need to find out what you did wrong and fix it. This is called debugging. There are some ways of debugging your code that can make it a lot easier. Let's look an a bit of example code. It calculates the sum of the numbers from 1 to 10:
n = 10;
sum = 0; i = 0;
while i < n
sum = sum + i;
i = i + 1;
end
disp(sum);
This code doesn't work. The result is supposed to be 1+2+3+4+5+6+7+8+9+10 = 55, but instead we get 45. How do we find the mistake?
Very simple debugging
The first method is to have your code print out the value of some variables while it's running. This can be as easy as taking away some semicolons:
n = 10;
sum = 0; i = 0;
while i < n
sum = sum + i
i = i + 1;
end
disp(sum);
This isn't very helpful so far, but it does show that the code seems to be stopping early. Let's try looking at the progress of another variable, by printing i each time through the loop:
n = 10;
sum = 0; i = 0;
while i < n
i
sum = sum + i;
i = i + 1;
end
disp(sum);
Run this and look at the output. What is the problem? Fix the code and include it in your lab report.
Stepping through your code
Another good way to find mistakes is to go through your program, line by line, and see what it's doing. Figure out what all the variables will be, and check if this matches your idea if what should happen.
Matlab has a way of doing this for you, called stepping. There's a "Step" command in the Debug menu, which you can also use by pressing F10. Try it out: open an M-file and step through it. Each time you step, Matlab runs one line of code. If you look at the variables in your workspace, they will show the values of the variables right now. This can really help you understand what a program is doing.
Here is more information on debugging M-files.
The assignment, part 1:
Write a function called splitfirst that will take an array and split it around the first occurrence of a given element. For example, if your function is given the array [3,1,4,1,5,9] and it told to split it around 4, then the function should return this matrix:
> splitfirst([3,1,4,1,5,9], 4)
ans =
3 1 0
1 5 9
Notice that the extra space was filled with a 0. Here's another example. If the function is given the array [1,2,3,4,5,6,7,8] and told to split it around 6, then it should return this matrix:
> splitfirst([1,2,3,4,5,6,7,8], 6)
ans =
1 2 3 4 5
7 8 0 0 0
The 6 has been removed, and everything after it has been moved into the next row. One final requirement: no empty rows! If you try to split at the beginning or end of an array, this should not produce a result with a row of all zeros. Instead, it should do something like this:
> splitfirst([1,2,3,4], 1)
ans =
2 3 4
Focus on getting your code working at all, then worry about this.
Writing the code for this problem is a little tricky, so you've been given a function called "above" in above.m which will help. The above function will take one matrix and put it on top of another, filling the remaining spaces with 0. To produce the first answer, you could do this:
> above([3,1], [1,5,9])
ans =
3 1 0
1 5 9
And for the second answer, you could do this:
> above([1,2,3,4,5], [7,8])
ans =
1 2 3 4 5
7 8 0 0 0
Above also works with arrays that have more than one row. For example:
> above([1, 2; 3, 4], [5,6,7,8])
ans =
1 2 0 0
3 4 0 0
5 6 7 8
Empty rows will be removed, which should be helpful:
> above([], [1,2,3])
ans =
1 2 3
Look at splitfirst.m and add code to do the splitting. Good luck.
Important note: you can test your code with the "testsplitfirst" function. Check it out:
> testsplitfirst
Testing splitfirst...
Failed: splitfirst([1,2,3,4], 1) should be [2,3,4]
Failed: splitfirst([1,2,3,4], 4) should be [2,3,4]
Passed 4/6 tests.
Your goal is to get it to pass all the tests. Include your test results in your lab report.
The assignment, part 2:
If you got this far, then congratulations on completing part 1. Part 2 is to split an array by every occurrence of a given element, not just the first one. For example:
> splitevery([3,1,4,1,5,9], 1)
ans =
3 0
4 0
5 9
Here, every time a 1 appears in the sequence, everything following it is moved down another row. As before, extra space is filled with zeros. Here's another example:
> splitevery([1,2,3,4,5,6,7,8], 4)
ans =
1 2 3 0
5 6 7 8
Since the 4 only appears once, this has exactly the same effect as using splitfirst. Here's an even more extreme case:
> splitevery([1,2,3,4,5], 9)
ans = 1 2 3 4 5
Since 9 doesn't occur anywhere in the array, it doesn't get split.
As in part 1, there should be no rows filled with just zeros. Here's an example that illustrates what's supposed to happen:
> splitevery([5, 4, 3, 5, 5, 5, 5, 1, 2, 5], 5)
ans =
4 3
1 2
Look at the file splitevery.m and add code. You can test it with the "testsplitevery" function:
> testsplitevery
Testing splitevery...
Passed 4 out of 4 tests.
Include your test results in your lab report.
No comments:
Post a Comment