150 likes | 229 Views
“But it looks right”: Bugs in non-majors media programs. Mark Guzdial College of Computing/GVU Georgia Institute of Technology. Context of Course. Required introduction to computing and programming in Python for Liberal Arts, Biology, Architecture, and Management majors.
E N D
“But it looks right”:Bugs in non-majors media programs Mark Guzdial College of Computing/GVU Georgia Institute of Technology
Context of Course • Required introduction to computing and programming in Python for Liberal Arts, Biology, Architecture, and Management majors. • Students learn to program by implementing Photoshop-style filters, splicing/reversing sounds, generating/searching Web pages, and creating animations. • Emphasis on reuse and program modification
“Invisible” bugs we’re focusing on • “They’re just small changes” • “Seemingly appropriate patches” • Bugs that students fall into by making changes that they consider small, insignificant. • The code always looks right • Usually an indicator of not understanding the change or the original program. • May also be an indicator an inappropriate user model (where the user may be the TA)
Example 1: From Testingto Meeting Requirements • Homework #1 is to posterize a picture (reduce colors) in a specifically-defined way. • The assignment requires the function hw1() to accept a picture object as input.
Student’s attempt • They get the function working like this, for ease of testing: def hw1(): file = pickAFile() picture = makePicture(file) # Posterizing goes here show(picture) • Then, when they get ready to turn it in, they change it like this to meet requirements: def hw1(picture): file = pickAFile() picture = makePicture(file) # Posterizing goes here show(picture) “But why won’t it work with the input image?”
Two problems at work here • Yes, the students don’t understand (at this point) how function inputs work. • But there’s also a misunderstanding that the user (the grader) knows to call the homework function with an input. “The homework says that the function hw1 should accept a picture as input...does that mean that the user will type in pict=pickAFile(), picture=makePicture(), and then hw1(picture), or do we need another function to do this for the user?” “Do we need to have our function show the picture or save it (as a file) as well? But if we don't show the picture, how will the TAs see the final image?”
Example 2: Iteratively making small changes • Homework #3 requires creating an audio collage. • Must splice two different sounds together. • Must compose one of the sounds twice, the second time with some kind of modification: Reversed, volume manipulation, frequency manipulation, sub-spliced, etc. Soup Stephen Hawking
We give them backwardSound() def backwardSound(filename): source = makeSound(filename) dest = makeSound(filename) sourceIndex = getLength(source) for destIndex in range(1, getLength(dest)+1): sample = getSampleValueAt(source, sourceIndex) setSampleValueAt(dest, destIndex, sample) sourceIndex = sourceIndex - 1 return dest
Student’s attempt def hw3(): … backsound=backwards(sound1) … def backwards(file): source = file target = file sourceIndex = getLength(source) for targetIndex in range(1,getLength(target)+1): sourceValue = getSampleValueAt(source,sourceIndex) setSampleValueAt(target,targetIndex,sourceValue) sourceIndex = sourceIndex -1 Note that the previous code handed in a file, to make two distinct sounds from. That’s been lost here. Was that return really all that necessary?
Getting lost in the debugging process • The code that we’re seeing from the student is actually several iterations down a debugging path. • The strategy the student used was to remove extraneous code—not a bad strategy. • The original backwards use of making a sound (to make distinct source/target sounds) seemed redundant, so that was removed. • Return disappeared since that didn’t seem to do anything useful! • At this point, the student has generated such buggy code that it’s hard to get back.
Indicating misunderstanding of the original program • The student didn’t understand what return did (very common among the non-majors before half-way through the term) nor why two copies of the sound were made. • Rather than try to understand what was confusing, she simply removed it. • Each change seemed small, so at each step, the code looked just fine.
def hw3(): #Establish sound files file = getMediaPath("canvas.wav") dest = makeSound(file) file1 = getMediaPath("gg_boom.wav") boom = makeSound(file1) file2 = getMediaPath("sh_madman.wav") madman = makeSound(file2) file3 = getMediaPath("bj_mommy.wav") #"Mommy mommy mommy" with volume changes #Add quiet volume "mommy" quietMommy = makeSound(file3) for sample in getSamples(quietMommy): value = getSample(sample) setSample(sample, value*0.5) return quietMommy source = quietMommy destSample = 1 for srcSample in range(4257, 8133): sampleValue = getSampleValueAt(source, srcSample) setSampleValueAt(dest, destSample, sampleValue) destSample = destSample + 1 return dest … There were 6 more loops and 6 more returns after this. Again, the student didn’t really understand the role of the original functions that he’s combining into his audio collage. So, he simply copied EVERYTHING—including each and every return Each loop looked like the original example function, so it must be right! Example 2b: Too many returns
Example 3: Where looking right shouldbe enough • Python uses indentation to indicate block structure. • No curly braces, no BEGIN..END • When it works well, the advantage is that if the code looks right, it is right • But in long programs with nested loops, it’s pretty easy to be off by a space somewhere
def hw2(): puppy=makePicture(getMediaPath("puppy.jpg")) print puppy canvas=makePicture(getMediaPath("7inX95in.jpg")) print canvas #First picture, at left edge targetX=1 for sourceX in range(1,getWidth(puppy)): targetY=getHeight(canvas)-getHeight(puppy)-5 for sourceY in range(1,getHeight(puppy)): px=getPixel(puppy,sourceX,sourceY) cx=getPixel(canvas,targetX,targetY) setColor(cx,getColor(px)) targetY=targetY + 1 targetX=targetX + 1 #Second picture, puppy negated negative(puppy) targetX=122 for sourceX in range(1,getWidth(puppy)): targetY=getHeight(canvas)-getHeight(puppy)-5 for sourceY in range(1,getHeight(puppy)): px=getPixel(puppy,sourceX,sourceY) cx=getPixel(canvas,targetX,targetY) setColor(cx,getColor(px)) targetY=targetY + 1 targetX=targetX + 1 Do you see it? The second loop never got indented back out to the level of the function block, so the next two loops are actually inside the for sourceY loop
Suggestion: Relying on “Looking right” passes • Relying on code “looking right” is a developmental stage. • The problems described here are common in the first three homework assignments in our class (first six weeks). • By week 15 (five programs later), they don’t happen. • Students learn not to trust what the code looks like • But understanding these early misconceptions may help us ease the transition for the students. • And maybe keep more students in CS