Teaching – Why Do Textbooks Use Pseudocode Rather Than Real Languages?

algorithmshaskellpseudocodepythonteaching

In colleges and in algorithm textbooks, it is quite common for the teacher and author to explain control flow in pseudo-code. With the advent of more expressive languages like Python and Haskell among others, is it reasonable that colleges switch to explain algorithms via one of these languages?

The only advantage of pseudo-code I can think of is that it's purportedly language-agnostic. But it is not. Some pseudo-code uses an imperative approach, other pseudo-code looks functional. Authors just borrow semantics from whatever programming language they are comfortable using, or worse just describe the semantics in natural language. So if pseudo-code isn't actually language-agnostic, what is the advantage of using it then? Wouldn't it be better to just use an existing language?

Best Answer

No. The point of pseudo-code is that it doesn't have to compile. I can quickly gloss over irrelevant details. In contrast, even languages that look like pseudocode at the first glance can have very non-intuitive details that would just detract from the algorithm. Let's take for example Quicksort in Haskell:

qs :: Ord a => [a] -> [a]
qs [] = []
qs (pivot:xs) = (qs smaller) ++ pivot:(qs larger)
  where smaller = [x | x <- xs, x <= pivot]
        larger  = [x | x <- xs, x > pivot]

or the same in Python:

def qs(array):
  if not array:
    return []
  pivot = array[0]
  xs = array[1:]
  smaller = [x for x in xs if x <= pivot]
  larger  = [x for x in xs if x > pivot]
  return qs(smaller) + [pivot] + qs(larger)

The advantage in both cases is that this is executable code, and as such can be tested, typechecked, and toyed with by students. However, they both include syntactic details that are distracting. Students would usually be better served by pseudocode that illustrates the intention of the algorithm, not implementation details:

algorithm QUICKSORT(array)
  return [] if array is empty
  pivot ← array[0]
  xs ← array[1, ...] -- the rest of the array without the pivot
  smaller ← [x | x ∈ xs, x <= pivot] -- all smaller or equal elements
  larger ← [x | x ∈ xs, x  > pivot] -- all larger elements
  return [QUICKSORT(smaller)..., pivot, QUICKSORT(larger)...]

Notable differences:

  • I can just make up a list comprehension syntax that looks like maths rather than having to explain why Python has a for and if here.

  • I don't have to explain that language's syntax for list concatenation. Why does Python use + addition? What is : in Haskell? I can just choose a syntax that gets the point across more clearly.

  • the type signature Ord a => [a] -> [a] is just an implementation detail. While possibly helpful in this case, the type signatures sometimes required by Haskell can get absurd.

  • I don't have to explain why Python considers empty collections to be false, and what array[1:] is supposed to mean.

  • I avoid clever students pointing out that I should really use yield in the Python example.

  • Haskell sucks for explaining mutable data structures like Hash Tables, RB trees, ….

  • Things start getting very language-specific once we need complex records to express our algorithms. E.g. Python's object system has a few surprises that are just distracting.

That said, it can be very valuable to use one of these languages in addition to pseudocode, just carefully label what is what.