Background and Explanation:

Problem-centric Programming Instruction(PcPI)

The information here discusses and explains our thinking with respect to problem-centric programming instruction. PcPI focusses on using tasks and problems in/with context when teaching programming. Those tasks and problems should be chosen with some care depending on the goal of the instruction. For example, a class for a general audience (non-CS majors) should probably use a variety of tasks/problems that would be familiar to or might be encountered by people in everyday life. For computer science majors, on the other hand, the tasks/problems could be chosen because they address ideas or problems where conceptual understanding of CS can be enhanced through that programming, e.g., parsing and evaluating an expression, converting a number to its corresponding digits, etc. In both cases, the problems should address the spectrum of data (numbers, strings, booleans, and combinations/collections of them).

We believe the instructional process can be the same for majors and non-majors. At the moment our work is focussed on problems for the general audience which, we think, can also be used for majors, particularly in the first programming course.

The goal in our classes is that students be able to produce substantial programs, i.e., several (3-5+) pages of code that utilize the gamut of programming essentials—basic data & actions; sequence, selection, repetition, data collections, and files. Getting to that point from a starting position of no programming background is problematic. At the moment we not sure of the best way to start working on the big problems we expect students to be able to handle at the end of the course. There are several competing notions:

We continue to work at improving what we do. As we gain further insight we will update this document to share it and report it via various venues. Feel free to examine prior papers and presentations relating to this work. Let us know if you have questions, comments, or suggestions?

The rest of this page addresses pedagogical suggestions and rationales. We believe they are necessary for a full understanding of our approach.

Why Can't I Write This Program?

One element of our instructional approach that we feel is particularly useful for teaching programming is the question, "Why can't I (you) write this program?" We try to encourage students to ask it of themselves when they encounter a new problem or experience difficulties. There are several possible responses to this question with suggested action when an answer is recognized.

Without this or some other mechanism to provide guidance for the programming process, students are left to a guess-and-check development process that is inefficient, prone to bad habit formation, and ineffective.

Problem Solving Pedagogy

Problems can be used in various ways in instruction. Similarly, the Why can't I write this program? question can be used at various times. Pedagogical goals and activities listed below seem obvious but may not include all uses. It seems reasonable to explicitly choose problems for a some reason or combination of reasons rather than using them randomly (though a random selection for assessment does seem reasonable). And, it seems reasonable to ask our key question in most instructional situations.

Note that merely using problems-in-context, rather than "problems" with solution specifications, is a key part of including problem solving in instruction; as is providing students with an explicit process to guide their problem solving.

Problem Organization/Categorizations

We think the selection of problems addressed in a programming class matters—that it affects the particular skills students develop and the way their understanding of programming becomes generalized. Thus, when selecting problems it is useful to explicitly consider your learning goals and the problems that might be used to help achieve those goals.

Of critical importance is that all the fundamental concepts of programming are addressed. We believe those to consist of  1) problem and data representation,  2) using the sequence code organization principal,  3) using the selection code organization principal,  4) using the repetition code organization principal, and  5) using the modularization code organization principal. To us, another key insight has been that addressing conditionals, separate from selection (and repetition), is necessary for developing programming skill. Finally, the problem list is organized from relatively simple tasks to fairly substantial problems. We are not sure that is the only or best or even a good way to proceed but if fits with what we are used to and will be used until we are able to say something more definitive about the organization. The discussion below addresses these ideas in more detail.

Programming Fundamentals

In our minds all programming instruction should address all the fundamental concepts of programming. Those concepts include basic actions (on data) the computer can take and the control structures that organize the actions. The tricky part of PcPI is to address these concepts while keeping the focus on problems rather than language features.

Conditionals & Selection

We consider the formulation of conditionals to be one of the programming skills that is hardest to develop. Students have encountered in their daily lives both selection and repetition where conditionals are most often used. Students can readily understand the concepts of choosing between (sets of) actions and repeating (sets of) actions. They are not, however, familiar with 1) representing problems formally using variables, particularly Boolean variables, 2) thinking in terms of variables and operations on variables, and 3) working with non-numeric operators—string operations/functions, relational operators, and logical operators. Many textbooks don't even introduce such concepts and operators until after introducing if statements. And, they provide little explicit practice on formulating conditional expressions.

A key idea in our work is that programs often need to ask questions of the data. Formulating these questions and developing appropriate conditional/Boolean expressions to answer the questions needs to be an explicit part of programming instruction.

We encountered the notion of roles of variables (see The Roles of Variables Home Page for more information). We have not yet incorporated that concept into our thinking but it did spur some thinking about categories for/classifications of conditional and selection contexts. We think there are useful insights in this thinking but caution against formally including it as a topic of instruction. Too often such ideas become a focus of instruction rather than a guide for planning, i.e., we mostly think it is not useful to "teach" directly (expect students to regurgitate them back) the classification schemes. We do think it is probably useful to provide some sort of handle that will allow students to think about the contexts and facilitate generalization.

Abstraction is a key part of programming. The most fundamental aspect of abstraction is naming a concept or thing and then using the name of the thing rather than describing it, e.g., colors, people, program modules, etc. A classification scheme/taxonomy of conditional expressions can be useful for students who are learning to program. However, the most important use of a taxonomy of problems is for teachers as they work to provide a comprehensive set of practice activities for learning to develop conditional expressions.

An initial taxonomy of conditional expressions

Our initial taxonomy includes types of conditionals on one axis and the types of data on the second axis. It is critical that students see examples on both continua of problem characteristics. A table version of the taxonomy includes all the basic data elements—numeric, string, and Boolean data. An extra category of derived (computed) data is also included to remind us that the Boolean expressions should contain a variety of data beyond simple variables and literals. The types of problems are shown below.

Please note that this is a very preliminary taxonomy. We anticipate performing further study and, perhaps, refinements. It should, however, provide a good starting place for producing a reasonably comprehensive set of conditional problems.

Presenting the problems

For learning purposes, we believe it is important to present the problems in a non-technical manner, i.e., in everyday prose without specifications and avoiding keywords like "if", "for each", etc. This enhances the focus on problem solving.

For conditional problems it is important to pay attention to the phrasing of the problems and to address alternative phrasings. This will help students learn that the words used often suggest a particular solution path but alternatives exist and might provide a better or more understandable (to them) solution. For example, "Is regular pay appropriate?" and "Do I need to include overtime when calculating pay?" are both reasonable questions to ask of the data in the "calculate gross pay" problem.

We recommend using this approach to directly address the notion of alternatives in the phrasings of questions and conditional expressions used to answer the questions (rather than "teaching" De Morgan's laws, though one might say in passing "For the math adepts this applies De Morgan's laws.").

Conditionals selection, but

Conditionals and selection are not the same but they are closely related. Selection makes use of conditionals certainly. But selection can also be used to provide alternatives to conditionals, particularly complex ones. For example, indicating whether a year is a leap year can be accomplished by

    if ((year mod 400 == 0) or (year mod 4 == 0 and year mod 100 != 0)
    {	display "leap year" 
    {	display "NOT a leap year" 

or by

    if (year mod 400 == 0) 
    {	display "leap year" 
    elseif (year mod 100 == 0)
    {	display "NOT a leap year" 
    elseif (year mod 4 == 0 )
    {	display "leap year" 
    {	display "NOT a leap year" 

The former might be preferable to most CS folks but the latter might be preferable (more understandable, easier to produce correctly) to many students. And, it is just as correct. And, for beginning programming students it should be okay.

We encourage demonstrating that complex conditionals can be simplified by using nested if statements. One reason is that students may find them simpler to understand. Another reason is that doing so may well enhance student facility with selection/if statements. Having student produce equivalences in both styles will enhance their understanding of both and show them good alternatives for producing correct code. It seems certain that code understanding and code correctness are positively correlated.

Problem Size and Sequence

Our original idea was to walk into class with a list of problems. We had no clear idea of what the problems might look like. Since then we have begun categorizing problems. One characteristic we use is size.

The notion of programming problems is somewhat problematic. For many people the task of converting a temperature value expressed in Fahrenheit to one expressed in degrees Celcius is at most a minor "task". But for some people it could amount to a fairly difficult task or problem. Certainly, novice programmers who doubt their math skills and perhaps are unfamiliar with multiple temperature systems might well consider writing a program to convert such temperatures to be a "problem". The discussion here assumes that even though we (computing teachers) would consider some activities to be trivial tasks, students likely need to encounter such tasks so they can develop their understanding of problem representation using variables and the fundamental actions of programs (input, process, & output).

We can imagine approaches (e.g., worked examples) that start with and mostly continue with big problems though perhaps focussing on smaller parts of them. That is not how we organize our classes, however. And everyone is different and needs to use an approach they are comfortable with. We tend to sequence our problems by focussing on the fundamental concepts, i.e., 1) start with understanding basic data and problem representation and sequencing actions on the data/representation; 2) move on to asking questions of the data; 3) address problems requiring selection; 4) work on problems requiring repetition; 5) work modularization in when it fits; and 6) address collections of data when needed but certainly with the repetition problems.

So, we tend to start with "small problems" which are "tasks" when encountered later. Of course the selection problems will be larger than most sequence problems. Similarly repetition problems will be larger than sequence problems. Thus, the problems will naturally grow in size as we progress through the programming fundamentals.

We focus on three sizes of problems depending on their use.