1 SequenceRecursion


1.1 Review lecture

../../ComputationalThinking/Content/Recursion.html

1.2 Reading

Discrete Mathematics using a Comptuter (O’Donnell, Hall, Page) Chapter 3

Discrete Mathematics with Applications - Metric Edition (Epp) Chapter 5

The Haskell Road to Logic, Math and Programming (Doets, van Eijck) 7, 10, 11

https://runestone.academy/ns/books/published/dmoi-4/ch_sequences.html
https://runestone.academy/ns/books/published/dmoi-4/sec_seq_intro.html
https://runestone.academy/ns/books/published/dmoi-4/sec_seq-growth.html
https://runestone.academy/ns/books/published/dmoi-4/sec_seq-polynomial.html
https://runestone.academy/ns/books/published/dmoi-4/sec_seq-exponential.html

https://runestone.academy/ns/books/published/ads/chapter_8.html
https://runestone.academy/ns/books/published/ads/s-faces-of-recursion.html
https://runestone.academy/ns/books/published/ads/s-Sequences.html
https://runestone.academy/ns/books/published/ads/s-recurrence-relations.html
https://runestone.academy/ns/books/published/ads/s-some-common-rrs.html
https://runestone.academy/ns/books/published/ads/s-generating-functions.html

https://runestone.academy/ns/books/published/dmoi-4/sec_addtops-genfun.html

1.3 Recursion over lists

Generally for linked lists, it is efficient to:
test for empty as the base case,
pull off the head of the list,
and optionally operate on it,
with a recursion on the tail:

f :: [a] -> type of result
f [] = result for empty list
f (x : xs) = result defined using (f xs) and x

More generally, it is possible to:
test for empty as the base case,
take an arbitrary element,
split a list in larger chunk(s),
and recurse on those chunks.

See QuickSort example below.

1.4 Peano arithmetic

https://en.wikipedia.org/wiki/Peano_axioms
The first axiom states that the constant 0 is a natural number:

  1. 0 is a natural number.

Peano’s original formulation of the axioms used 1 instead of 0 as the “first” natural number,
while the axioms in Formulario mathematico include zero.

The next four axioms describe the equality relation.
Since they are logically valid in first-order logic with equality,
they are not considered to be part of “the Peano axioms” in modern treatments.

  1. For every natural number x, x = x.
    That is, equality is reflexive.

  2. For all natural numbers x and y, if x = y, then y = x.
    That is, equality is symmetric.

  3. For all natural numbers x, y and z, if x = y and y = z, then x = z.
    That is, equality is transitive.

  4. For all a and b, if b is a natural number and a = b, then a is also a natural number.
    That is, the natural numbers are closed under equality.

The remaining axioms define the arithmetical properties of the natural numbers.
The naturals are assumed to be closed under a single-valued “successor” function S.

  1. For every natural number n, S(n) is a natural number.
    That is, the natural numbers are closed under S.

  2. For all natural numbers m and n, if S(m) = S(n), then m = n.
    That is, S is an injection.

  3. For every natural number n, S(n) = 0 is false.
    That is, there is no natural number whose successor is 0.

Axioms 1, 6, 7, 8 define a unary representation of the intuitive notion of natural numbers:
the number 1 can be defined as S(0), 2 as S(S(0)), etc.
However, considering the notion of natural numbers as being defined by these axioms,
axioms 1, 6, 7, 8 do not imply that the successor function generates all the natural numbers different from 0.

The intuitive notion that each natural number can be obtained,
by applying successor sufficiently many times to zero,
requires an additional axiom,
which is sometimes called the axiom of induction.

  1. If K is a set such that:
    0 is in K, and
    for every natural number n, n being in K implies that S(n) is in K,
    then K contains every natural number.

1.5 Code

Code-DMUC/Stdm03Recursion.hs

-- # Software Tools for Discrete Mathematics
module Stdm03Recursion where

-- # Chapter 3.  Recursion

-- |
-- >>> factorial 5
-- 120
factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n - 1)

-- | Built in: length
-- >>> lengthList [1,2,3,4]
-- 4
lengthList :: [a] -> Int
lengthList [] = 0
lengthList (x : xs) = 1 + lengthList xs

-- | Bulit in: sum
-- >>> sumList [1,2,3,4]
-- 10
sumList :: (Num a) => [a] -> a
sumList [] = 0
sumList (x : xs) = x + sumList xs

-- |
-- >>> [1,2,3] ++++ [4,5,6]
-- [1,2,3,4,5,6]
(++++) :: [a] -> [a] -> [a]
[] ++++ ys = ys
xs ++++ [] = xs
(x : xs) ++++ ys = x : (xs <> ys)

-- | Built in: zip
-- >>> zipList [1,2] ['a', 'b']
-- [(1,'a'),(2,'b')]
zipList :: [a] -> [b] -> [(a, b)]
zipList [] ys = []
zipList xs [] = []
zipList (x : xs) (y : ys) = (x, y) : zipList xs ys

-- | Built in: concat
-- >>> concatList [[1,2,3],[4,5,6]]
-- [1,2,3,4,5,6]
concatList :: [[a]] -> [a]
concatList [] = []
concatList (xs : xss) = xs ++ concatList xss

-- | Built in: intercalate
-- >>>
-- 
-- intercalateList = 

-- | This is a classic elegant Haskell example.
-- >>> quickSort [34,2,13,51,23,2,490]
-- [2,2,13,23,34,51,490]
quickSort :: (Ord a) => [a] -> [a]
quickSort [] = []
quickSort (x : xs) = quickSort [y | y <- xs, y <= x] ++ [x] ++ quickSort [y | y <- xs, y > x]

-- | This is a classic elegant Haskell example.
-- >>> quickSort3 [34,2,13,51,23,2,490]
-- [2,2,13,23,34,51,490]
quickSort3 :: (Ord a) => [a] -> [a]
quickSort3 [] = []
quickSort3 (x : xs) =
  let lesser = filter (< x) xs
      greater = filter (>= x) xs
   in (quickSort3 lesser) ++ [x] ++ (quickSort3 greater)

-- | This is a classic elegant Haskell example.
-- >>> quickSort3 [34,2,13,51,23,2,490]
-- [2,2,13,23,34,51,490]
quickSort2 :: (Ord a) => [a] -> [a]
quickSort2 [] = []
quickSort2 (x : xs) = quickSort2 lesser ++ [x] ++ quickSort2 greater
  where
    lesser = [n | n <- xs, n < x]
    greater = [n | n <- xs, n >= x]

-- https://stackoverflow.com/questions/932721/efficient-heaps-in-purely-functional-languages
-- https://rosettacode.org/wiki/Sorting_algorithms/Heapsort#Haskell
-- https://gist.github.com/mmagm/2694665

-- Higher order functions that take a function as an argument:

-- | Built in: zip
-- >>> mapList (5 *) [1,2,3]
-- [5,10,15]
mapList :: (a -> b) -> [a] -> [b]
mapList f [] = []
mapList f (x : xs) = f x : mapList f xs

-- | Bulit in: elem
-- >>> elemList 4 [1,2,3,4]
-- True
elemList :: (Eq a) => a -> [a] -> Bool
elemList element [] = False
elemList element (x : xs)
  | element == x = True
  | otherwise = elemList element xs

-- | Built in: filter
-- >>> filterList even [1,2,3,4,5]
-- [2,4]
filterList :: (a -> Bool) -> [a] -> [a]
filterList f [] = []
filterList f (x : xs)
  | f x = x : filterList f xs
  | otherwise = filterList f xs

-- | Built in: find
-- >>> findList (==5) [4,5]
-- Just 5
findList :: (a -> Bool) -> [a] -> Maybe a
findList f [] = Nothing
findList f (x : xs)
  | f x = Just x
  | otherwise = findList f xs

findIndexRec :: Int -> (a -> Bool) -> [a] -> Maybe Int
findIndexRec n f [] = Nothing
findIndexRec n f (x : xs)
  | f x = Just n
  | otherwise = findIndexRec (n + 1) f xs

-- | Built in: findIndex
-- >>> findIndexList (==5) [3,4,5]
-- Just 2
findIndexList = findIndexRec 0

-- | Built in: elemIndex
-- >>> elemIndexList 5 [1,2,3,4,5]
-- Just 4
elemIndexList :: (Eq a) => a -> [a] -> Maybe Int
elemIndexList element = findIndexList (element ==)

-- | Built in: zipWidth
-- >>> zipWithList (+) [1,2,3] [4,5,6]
-- [5,7,9]
zipWithList :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWithList f [] ys = []
zipWithList f xs [] = []
zipWithList f (x : xs) (y : ys) = f x y : zipWithList f xs ys

-- | Built in: foldr
-- >>> foldrList (+) 0 [1,2,3]
-- 6
foldrList :: (a -> b -> b) -> b -> [a] -> b
foldrList f accumulator [] = accumulator
foldrList f accumulator (x : xs) = f x (foldrList f accumulator xs)

-- Some functions written in terms of foldr

-- | Built-in: sum
-- >>> sumList2 [1,2,3]
-- 6
sumList2 xs = foldr (+) 0 xs

-- | Built-in: product
-- >>> productList [1,2,3,4]
-- 24
productList xs = foldr (*) 1 xs

-- | Built-in: and
-- >>> andList [True, True, False]
-- False

-- | Built-in: and
-- >>> andList [True, True, True]
-- True
andList xs = foldr (&&) True xs

-- | Built-in: or
-- >>> orList [False, False, True]
-- True

-- | Built-in: or
-- >>> orList [False, False, False]
-- False
orList xs = foldr (||) False xs

-- |
-- >>> factorial2 5
-- 120
factorial2 n = foldr (*) 1 [1 .. n]

-- |
-- >>> firsts1 [(1,3),(2,4)]
-- [1,2]
firsts1 :: [(a, b)] -> [a]
firsts1 [] = []
firsts1 ((a, b) : ps) = a : firsts1 ps

-- | Demonstration of build in: fst
-- >>> fst (1,2)
-- 1

-- |
-- >>> firsts2 [(1,3),(2,4)]
-- [1,2]
firsts2 :: [(a, b)] -> [a]
firsts2 xs = map fst xs

-- Peano Arithmetic
-- Zero is 0
-- Succ is short for successor
-- Review this now: -- https://wiki.haskell.org/Algebraic_data_type
-- See this now, for review: ../Haskell/DataExamples.hs

data Peano = Zero | Succ Peano
  deriving (Show, Eq)

-- |
-- >>> one
-- Succ Zero
one = Succ Zero

-- |
-- >>> two
-- Succ (Succ Zero)
two = Succ one

-- |
-- >>> three
-- Succ (Succ (Succ Zero))
three = Succ two

-- |
-- >>> four
-- Succ (Succ (Succ (Succ Zero)))
four = Succ three

-- |
-- >>> five
-- Succ (Succ (Succ (Succ (Succ Zero))))
five = Succ four

-- |
-- >>> six
-- Succ (Succ (Succ (Succ (Succ (Succ Zero)))))
six = Succ five

-- | This never finishes
-- >>> take 5 infinitePeanos
-- [Zero,Succ Zero,Succ (Succ Zero),Succ (Succ (Succ Zero)),Succ (Succ (Succ (Succ Zero)))]
infinitePeanos = Zero : map Succ infinitePeanos

-- |
-- >>> decrement (Succ (Succ Zero))
-- Succ Zero

-- |
-- >>> decrement six == five
-- True
decrement :: Peano -> Peano
decrement Zero = Zero
decrement (Succ a) = a

-- |
-- >>> add (Succ (Succ Zero)) (Succ (Succ Zero))
-- Succ (Succ (Succ (Succ Zero)))

-- |
-- >>> add five one == six
-- True
add :: Peano -> Peano -> Peano
add Zero b = b
add (Succ a) b = Succ (add a b)

-- |
-- >>> sub (Succ (Succ Zero)) (Succ Zero)
-- Succ Zero

-- |
-- >>> sub two one == one
-- True
sub :: Peano -> Peano -> Peano
sub a Zero = a
sub Zero b = Zero
sub (Succ a) (Succ b) = sub a b

-- |
-- >>> equals four (Succ (Succ (Succ (Succ Zero))))
-- True

-- |
-- >>> equals four four
-- True
equals :: Peano -> Peano -> Bool
equals Zero Zero = True
equals Zero b = False
equals a Zero = False
equals (Succ a) (Succ b) = equals a b

-- |
-- >>> lt three four
-- True

-- |
-- >>> lt four three
-- False
lt :: Peano -> Peano -> Bool
lt a Zero = False
lt Zero (Succ b) = True
lt (Succ a) (Succ b) = lt a b

-- Data recursion:

-- |
-- >>> take 5 (fDatarec 1)
-- [1,1,1,1,1]
fDatarec :: a -> [a]
fDatarec x = x : fDatarec x

-- | This actually consumes memory, because it chains.
-- >>> take 5 ones
-- [1,1,1,1,1]
ones = fDatarec 1

-- | This is circular in memory, and thus much more efficient than ones!
-- >>> take 5 twos
-- [2,2,2,2,2]
twos = 2 : twos

-- |
-- >>> take 10 object
-- [1,2,3,1,2,3,1,2,3,1]
object =
  let a = 1 : b
      b = 2 : c
      c = [3] ++ a
   in a