Saturday 4 May 2013

Bad assignments

So recently I've been doing a part-time university course. Programming, specifically the MIT OpenCourseWare course Introduction to Computer Science and Programming 6.00. And I'm a bit torn at the moment, because in some ways I'm really enjoying it - filling in gaps in all my self-taught programming knowledge, solving problems, learning stuff - and at other times it's incredibly frustrating. Sometimes, that's simply because I can't do something, let's be fair. At other times, though, it's because the assignments themselves throw up arbitrary obstacles.

Several times, I've run into assignments that seem to have been written by someone studying the code of their own program and basing the specs entirely on what they see. Questions give you information that's technically accurate, but deeply unhelpful in working out how the success of failure of your solution is going to be measured. On other occasions, you're building something that'll plug into a larger bit of code rather than working by itself, but the assignment doesn't make it easy to work out the mechanics of what it's doing and the ultimate purpose of the component.

Each trigger class you define should implement the following interface, either directly or transitively. It must implement the evaluate method that takes a news item (NewsStory object) as an input and returns True if an alert should be generated for that item. We will not use the implementation of the Trigger class (which is why it throws an exception should anyone attempt to use it), but rather the function definition that specifies that an evaluate(self, story) function should exist.

Implement a word trigger abstract class, WordTrigger. It should take in a string word as an argument to the class’s constructor.

WordTrigger should be a subclass of Trigger. It has one new method, is_word_in. is_word_in takes in one string argument text. It returns True if the whole word word is present in text, False otherwise, as described in the above examples. This method should not be case-sensitive. Implement this method.

Because this is an abstract class, we will not be directly instantiating any WordTriggers. WordTrigger should inherit its evaluate method from Trigger. We do this because now we can create subclasses of WordTrigger that use its is_word_in function. In this way, it is much like the Trigger interface, except now actual code from the class is used.

Let's pretend for a moment that this is an engineering course instead. It's like giving out an assignment like this:

In this assignment, you will design a vehicle capable of carrying four people and half a ton of luggage hundreds of miles by oxidising hydrocarbons in an exothermic reaction. The skeleton code produces a road and designates Driver, the person in charge of the vehicle: you do not need to understand this code.

1) Build a function Wheel that will rotate through 360 degrees in a single plane while maintaining contact with a surface. The function takes a single input, Axle.

2) Build a function Turner that will rotate an argument Turnee +/- 0-360 degrees around a pivot in a plane 90 degrees to the plane of Wheel's surface. The function takes two inputs, Rotation and Turnee, but cannot be used directly.

3) Build a function Steering, which will change the direction of travel of the car when a user moves their hands in a roughly circular motion. At this stage, your work should pass stages 1-6 of Vehicle Inspection.

4) Build a function Body, which assembles four Wheels, two Axles, your existing Turner and Steering code, and the module Engine (you do not need to understand this module). Now you have a functional car.

Now imagine doing that if you've never seen a car before, and if no details of Vehicle Inspection are publically available. At each test, you receive only a brusque statement like "Failed test 4 because the function turned right when it should have turned left." You must analyse these statements to establish exactly how it is the tester expects your function to operate, then rebuild and retest. You don't really know what kind of data is being fed into your functions, or how exactly they're supposed to relate to each other. Most of the time, you can't perform testing yourself, because most of the functions don't work in isolation.

While it's possible to eventually work things out, the requisite information is spread over several pages, and you really need to reread things multiple times - and possibly draw out secondary notes of your own - to find out what the question actually is. This is not good design.

At other times, they're wildly variable in how much support they offer. An early question suggests several (basic) methods you can use to solve (basic) difficulties, all of which have come up earlier. The final question entirely fails to mention that in order to achieve their requirements, you actually need to build a completely separate function that can build components for you and store information about them in an archive; a technique which has never been mentioned during the course. I eventually found that one out when I got the suggested solutions, having given up when I couldn't find any way to build items in a loop with names based on a variable to meet their requirements.

I don't mind writing programs, or solving problems. Those are fun things. I do mind when my progress stalls because I can't work out what's expected of me, or when a problem must be solved in a particular way, but this relies on secondary work that isn't even mentioned. I do mind when I can't really work out whether my code's doing what it needs to, because I neither fully understand what that is, nor am I able to test the code myself.

So yes, I'm really quite grumpy at the moment.

No comments:

Post a Comment