Base cases and identities

From HaskellWiki
Revision as of 09:12, 30 December 2008 by Henk-Jan van Tuyl (talk | contribs) (Added a link to Wikipedia for "endomorphism")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Sometimes it's hard to work out what the base case of a function should be. Sometimes you can work it out by examining the identities of your operations.

Examples

As a simple example, consider the function sum, which takes a list of numbers and adds them:

sum [] = ???
sum (x:xs) = x + sum xs

where `???` is yet to be determined. It's not obvious what the `sum` of an empty list should be, so let's try to work it out indirectly.

The sum function is about adding things. For non-degenerate cases at least, we want `sum` to obey these rules:

sum [x] == x
sum xs + sum ys == sum (xs ++ ys)

Substituting xs = [] and ys = [0] gives us:


    sum [] + sum [0] == sum ([] ++ [0])
 => sum [] + 0 == 0
 => sum [] == 0

...and there's our base case.

Similarly, for the `product` function:


    product [x] == x
    product xs * product ys == product (xs ++ ys)
 => product [] * product [1] == product ([] ++ [1])    -- (using xs = [], ys = [1])
 => product [] == 1

In both of these cases, the base case is the identity of the underlying operation. This is no accident, and the reason should be obvious:

    product [] * product [x] == product ([] ++ [x])
 => product [] * x == x

It follows that `product []` should be the identity for multiplication.

Sometimes there is no identity. Consider this function, for example, which returns the minimum value from a list:

    minimum [x] == x
    minimum xs `min` minimum ys == minimum (xs ++ ys)
 => minimum [] `min` minimum [x] == minimum ([] ++ [x])
 => minimum [] `min` x == x

The only sensible value for minimum [] is the maximum possible value for whatever type x has. Since there is no such value in general (consider x :: Integer, for example), minimum [] has no sensible value. Better to use a foldr1 or foldl1 pattern instead:

minimum [x] = x
minimum (x:xs) = x `min` minimum xs

Exercises

What are sensible base cases for these functions?

  • concat, which appends a list of lists (e.g. concat [[1],[],[2,3]] == [1,2,3]).
  • and, which takes a list of Bool values and logically "ands" (&&) them together.
  • or, which takes a list of Bool values and logically "ors" (||) them together.
  • xor, which takes a list of bool values and logically "exclusive ors" them together.
  • greatest_common_divisor, which returns the GCD of a list of integers. (The GCD of two integers is the largest number which divides evenly into them both.)
  • least_common_multiple, which returns the LCM of a list of integers. (The LCM of two integers is the smallest number which they both evenly divide into.)

-- User:AndrewBromage

  • compose, which composes a list of "endo"-functions e.g.:
compose [recip,(** 2),sin,(* 2 * pi)] = recip . (** 2) . sin . (* 2 * pi) = \x -> recip (sin (x * 2 * pi) ** 2)
("endo"-function meaning that the function returns something of the same type as it as it takes as input, (from endomorphism in category theory))

-- User:StefanLjungstrand