Start from the Middle: Making Programming Easier (Part 1)

V - Jul 29 - - Dev Community

Start from the Middle: Making Programming Easier (Part 1)

This post is a continuation of “Start from the Middle: How to Solve a Problem.”

Programming is challenging for many reasons, including the multitude of decisions a programmer must make during development. Often, when faced with a problem, it’s not even clear where to start. My advice is to start from the middle.

Problem-solving process

The previous post described a 'philosophical' view on problem-solving. The idea is that starting from the middle of a problem is advantageous because it provides a retrospective view — looking backward as if the problem, or part of it, has already been solved. This allows to make a better decision about the where to start. This also makes it easier to think about the next steps, as we can abstract from the details of how we got to the middle part and focus on the rest of the solution.

While this approach is universal and can be applied to numerous problems, how can we apply it to programming? Here’s the outline of the proposed method.

  • Look at the state in the middle of computation
  • Determine variables and their initial state
  • Determine the final condition
  • Code the rest

The Middle Part

Starting from the middle means looking at the problem as if some part of it has already been solved, visualizing what has been done so far and what remains to be done. What constitutes the middle of a computation depends on the program. To make things more concrete, let’s assume our program has a loop, and the loop is the central part of the algorithm. In this case, it is helpful to look at the state of the program at the beginning of the loop iteration.

What has been computed so far? What variables are needed to represent that work? What does the value of each variable represent? Is this information sufficient to complete the computation and obtain the result?

These are some questions we may need to ask ourselves.

Note: not all programs have loops, but the most useful and interesting ones use iteration/recursion

Trivial Example

For the rest of this article, we’ll assume the reader has basic programming knowledge and some familiarity with the Python programming language. Python has a simple syntax, and hopefully, readers can easily translate the examples here to their preferred language.

As the first example, we will do a list summation problem: writing a program that computes the sum of all the numbers in a list L. The result should be stored in the variable s:

s := ∑L

It’s clear that we need to look at each number in the list to find the sum, so we need a loop. Let’s imagine the state of the program in the middle of its execution.

What has been done so far? It is reasonable to think that we have a partial sum up to some index i of the list L. Therefore, we need the variable i to represent this.

Now, what exactly does the sum s represent at this point? Should the sum include the value at L[i] or not? Since it doesn’t seem to be important, we arbitrarily choose to exclude L[i] from the sum at the beginning of the iteration. With this interpretation, i represents the number of items processed so far. If it’s not clear why i represents the number of list items processed, consider the case when i=0. This interpretation of s affects the value of s and i at the beginning of the program.

Initial state. We initialize i with 0 because we haven’t processed any list items yet. Since there are no numbers before i=0, and the sum of an empty segment is 0, we initialize s with 0.

When should the computation stop? We assumed that we’re going to iterate through the list in a loop. We should stop the iteration when all numbers are included in the sum s. We remind ourselves again about the meaning of i — the number of list items processed so far. That means the program should stop when the whole list has been processed. In other words, the loop should continue while i remains less than the length of the list: i < len(L).

Here’s the program so far:

def sum_of(L):
   i = 0 # number of processed list items
   s = 0 # sum of i items in L

   while i < len(L):
     # 0 < i < len(L)
     # s = ∑ 0<n<i, L[n] -- sum of L items up to index i exclusive
     ...

   # i = len(L) -- after loop exits
   return s

Filling in the details. Let’s take a look at the body of the loop. To make progress, we need to increase i. The minimal step is to increase i by 1, so we add the i increment to the loop. We also need to update s before the i increment, not after. (Do you see why? Hint: remember what i means). We update the program.

def sum_of(L):
   i = 0 # number of processed list items
   s = 0 # sum of i items in L

   while i < len(L):
     # 0 < i < len(L)
     # s = ∑ 0<n<i, L[n] -- sum of L items up to index i exclusive

     s = s+L[i]
     i = i+1

   # i = len(L) -- after loop exits
   return s

This completes the development.

As an exercise, try to solve the list summation problem, but now interpret s as the sum of the list items in the range 0 to i inclusive. This will yield a different program.

Summary

Starting from the middle means looking at the problem as if some part of it has already been solved, visualizing what has been done so far and what remains to be done. Using a simple list summation problem, we highlighted the steps to iteratively build solutions using this method. Each step is justified, minimizing arbitrary decisions and promoting a systematic approach.

A common approach to programming involves guessing and debugging until the program works as expected. However, programming becomes much easier when approached methodically.

The method discussed here transforms arbitrary decision-making into a structured activity. This method is not limited to programming but can also be applied to any problem-solving task.


. . .
Terabox Video Player