I failed at reading RWH; and not one to quit, I ordered Haskell: The Craft of Functional Programming. Now I'm curious about these functional proofs on page 146. Specifically I'm trying to prove 8.5.1 sum (reverse xs) = sum xs. I can do some of the induction proof but then I get stuck..
HYP:
sum ( reverse xs ) = sum xs
BASE:
sum ( reverse [] ) = sum []
Left = sum ( [] ) (reverse.1)
= 0 (sum.1)
Right = 0 (sum.1)
INDUCTION:
sum ( reverse (x:xs) ) = sum (x:xs)
Left = sum ( reverse xs ++ [x] ) (reverse.2)
Right = sum (x:xs)
= x + sum xs (sum.2)
So now I'm just trying ot prove that Left sum ( reverse xs ++ [x] ) is equal to Right x + sum xs, but that isn't too far off from where I started sum ( reverse (x:xs) ) = sum (x:xs).
I'm not quite sure why this needs to be proved, it seems totally reasonable to use the symbolic proof of reverse x:y:z = z:y:x (by defn), and because + is commutative (arth) then reverse 1+2+3 = 3+2+1,
sum (reverse []) = sum [] -- def reverse
sum (reverse (x:xs)) = sum (reverse xs ++ [x]) -- def reverse
= sum (reverse xs) + sum [x] -- sum lemma below
= sum (reverse xs) + x -- def sum
= x + sum (reverse xs) -- commutativity assumption!
= x + sum xs -- inductive hypothesis
= sum (x:xs) -- definition of sum
However, there are underlying assumptions of associativity and commutativity that are not strictly warranted and this will not work properly for a number of numerical types such as Float and Double where those assumptions are violated.
Lemma: sum (xs ++ ys) == sum xs + sum ys given the associativity of (+)
Proof:
sum ([] ++ ys) = sum ys -- def (++)
= 0 + sum ys -- identity of addition
= sum [] ++ sum ys -- def sum
sum ((x:xs) ++ ys) = sum (x : (xs ++ ys)) -- def (++)
= x + sum (xs ++ ys) -- def sum
= x + (sum xs + sum ys) -- inductive hypothesis
= (x + sum xs) + sum ys -- associativity assumption!
= sum (x:xs) + sum ys -- def sum
Basically you need to show that
sum (reverse xs ++ [x]) = sum (reverse xs) + sum [x]
which then easily leads to
= x + sum (reverse xs)
= x + sum xs -- by inductive hyp.
The problem is to show that sum distributes over list concatenation.
Use the definition of a sum to break up (sum reverse xs ++[x]) into x + sum(reverse(xs)), and using your inductive hypothesis you know sum(reverse(xs)) = sum(xs). But I agree, induction is overkill for a problem like this.
Here's where I think you're stuck. You need to prove a lemma that says
sum (xs ++ ys) == sum xs + sum ys
To prove this law you will have to assume that addition is associative, which is true only for integers and rationals.
Then, you will also need to assume that addition is commutative, which is true for integers and rationals but also for floats.
Digression: The style of your proofs looks very strange to me. I think you will have an easier time writing these kinds of proofs if you use the style in Graham Hutton's book.
Related
I've written a simple function in haskell that is non tail recursive that sums up the values inside a list where:
nonTailRecursiveSum :: [Integer] -> Integer
nonTailRecursiveSum [] = 0 --base case
nonTailRecursiveSum (x:xs) = x + sum xs
But what I'm trying to do now is to implement the same function but using tail recursion. For what i know, tail recursion performs the recursive call at the final step so i tried something like:
tailRecursiveSum :: [Integer] -> Integer
tailRecursiveSum [] = 0
tailRecursiveSum (x:xs) = aux_f(x) + tailRecursiveSum xs
.
.
But i got lost in the midway as I'm not familiar with tail recursion in Haskell. Could anyone assist me on the continuation of the tail recursive version of the code?
Playing with it for a bit,
sum (x:y:xs) = x + sum (y:xs)
= x + (y + sum xs)
= (x + y) + sum xs
g a b = a + sum b
sum (x:y:xs) = g x (y:xs)
= x + g y xs
= g (x+y) xs -- !!!
the last one is in tail recursive form! We thus just define
sum xs = g 0 xs
where
g acc [] = ...
g acc (x:xs) = g (acc + ...) ...
Fill in the blanks!
Hi I'm quite new to haskell and just got into structural induction and was wondering if someone could explain the steps I should take it would be very helpful.
Question:
-- computes the sum of all numbers in the list
sum :: [Integer] -> Integer
sum [] = 0
sum (x:xs) = x + sum xs
-- appends two lists
(++) :: [Integer] -> [Integer] -> [Integer]
[] ++ ys = ys
(x:xs) ++ ys = x : (xs ++ ys)
Prove (by using structural induction) that the following equation holds, for all lists of integers xs and ys:
sum (xs ++ ys) = sum xs + sum ys
Don't forget to state the I.H. in the induction step. Please also make sure you clearly state the reasons why you make the steps in your proofs.
My steps:
To prove:
sum (xs ++ ys) = sum xs + sum ys
Proof: by structural induction
Let P ( ) <--- dont really know what to type in there so if someone could take it from there I would appreciete it greatly!
++ is defined by induction on its first argument, xs, so this is usually a good sign that we need to proceed by induction on xs.
Hence, we fix ys once for all, and define P(xs) as follows
P(xs) = (sum (xs ++ ys) == sum xs + sum ys)
Now you have to prove that P(xs) holds for all xs. Apply the induction principle on lists and you should be OK.
I'm learning Haskell at the moment and I read a book called "Thinking Functionally With Haskell" and I can't really understand why this expression from the first chapter is true:
sum . map sum = sum . concat
Informally, this is just saying that because if addition is associative, it doesn't matter how you group the numbers you are adding. (a + b) + (c + d) is the same as (a + b + c + d).
Formally, we can use equational reasoning and structure induction to prove this for lists of any size. (See the end for quick definitions of these two processes.)
Assuming the following definitions of map, concat, sum, and (.):
map sum [] = []
map sum (a:as) = sum a : map sum as
concat [] = []
concat (a:as) = a ++ concat as
sum [] = 0
sum (a:as) = a + sum as
(f . g) x = f (g x)
To make the proof below a little simpler, we'll claim without an explicit proof (but see below) that
sum (a ++ b) == sum a + sum b
First we establish that the identity is true for empty lists.
(sum . map sum) [] == sum (map sum []) -- (7)
== sum [] -- (1)
== sum (concat []) -- (3)
== (sum . concat) [] -- (7)
(Note that we don't need definition 5, since an empty list is an empty list.)
Now, add a new definition, for any list as of size k.
(sum . map sum) as == (sum . concat) as
If (9) is true, we can prove the identity for list of size k+1:
(sum . map sum) (a:as) == sum (map sum (a:as)) -- (7)
== sum (sum a : map sum as) -- (2)
== sum a + sum (map sum as) -- (6)
== sum a + (sum . map sum) as -- (7)
== sum a + (sum . concat) as -- (9)
== sum a + sum (concat as) -- (7)
== sum (a ++ concat as) -- (8)
== sum (concat (a:as)) -- (4)
== (sum . concat) (a:as) -- (7)
By induction, we have proved the sum . map sum == sum . concat for lists of any size.
Equational reasoning means that we can use an equality like a = b to replace a with b or b with a at any step of our proofs.
Structural induction on lists is a bootstrapping process. You assume some property is true for lists of size k, then use that to prove it is true for lists of size k+1. Then, if you can prove it is true for k=0, this implies it is true for all k. For example, if it is true for k=0, then it is true for k=1, which means it is true for k=2, etc.
Definition 4 assumes a definition of ++:
[] ++ bs = bs
(a:as) ++ bs = a : (as ++ bs)
With ++ defined, we can prove (8):
A base case: a is empty
sum ([] ++ b) == sum b -- definition of ++
== 0 + sum b -- definition of +
== sum [] + sum b -- definition of sum
Assuming sum (a++b) is true for a of length k,
sum ((a:as) ++ bs) == sum (a : (as ++ bs)) -- definition of ++
== a + sum (as ++ bs) -- definition of sum
== a + sum as + sum bs -- induction
== sum (a:as) + sum bs -- definition of sum
Imagine we have a list:
myList :: [[Int]]
myList = [[1,2],[3,4,5]]
Let's apply sum . map sum:
(sum . map sum) [[1,2],[3,4,5]]
= sum [sum [1,2], sum [3,4,5]]
= sum [1+2,3+4+5]
= 1+2+3+4+5
Now let's apply sum . concat:
(sum . concat) [[1,2],[3,4,5]]
= sum [1,2,3,4,5]
= 1+2+3+4+5
Hopefully you can see now that, because (a+b)+c = a+(b+c), the order in which we add things does not matter, thus summing the inner lists, then summing the entire list produces the same result as simply summing each value of the inner lists.
I want to reorder a list in the following way:
[5,6,7,8,9] -> [7,5,9,6,8]
[6,7,8,5,4,3] -> [8,5,6,3,7,4]
It's supposed to get the middle number or numbers of the list and put them in the starting position. After that it should start to get the two outer numbers of the list and add them in and work its way in.
I have the following code to get the middle numbers and put them into the beginning of the list but can't figure out how to start adding the outer numbers into the new list.
-- import Data.List
-- import System.IO
longitude xs = length xs
middle xs = length xs `div` 2
addOne xs = middle xs - 1
oneMore xs = length xs - 1
otherCase xs = oneMore xs `div` 2
valuea xs = xs !! middle xs
valueb xs = xs !! addOne xs
valuec xs = xs !! otherCase xs
modulus xs = longitude xs `mod` 2
order xs = midNums xs
takes xs = take (otherCase xs) xs
oddOne xs = otherCase xs + 1
takeX xs = drop (oddOne xs) xs
value xs = takes xs ++ takeX xs
reorder xs = drop (otherCase xs )(take (middle xs + 1) xs)
valueOdd xs = reorder xs ++ takes xs ++ takeX xs
paruno xs = drop (middle xs + 1) xs
pairTwo xs = take (addOne xs) xs
midPair xs = take (addOne xs)(drop (middle xs -1) xs)
--Get the numbers
midNums xs = if modulus xs == 0 then midPair xs ++ paruno xs ++ pairTwo xs
else valueOdd xs
I want it to work like this: Demo
Try this:
f :: (Num a) => [a] -> [a]
f [] = []
f [x] = [x]
f xs = if len `mod` 2 == 1 then flatten [xs !! half] else flatten [xs !! (half-1), xs !! half]
where len = length xs
half = len `div` 2
firsthalf = take (half-1) xs
secondhalf = (reverse . take half . drop (half+1)) xs
outtoin = zipWith (\x y -> x:y:[]) firsthalf secondhalf
flatten = concat . flip (:) outtoin
Breaking it down:
First get the midpoint(s)
Next get the two halves of the list excluding middle elements
Build the list from outside inwards using zip
Concatenate the zip result to flatten and add to the middle elements list
Demo
Attempting to understand the differences between Clojure and Haskell. I have the following code which calculates the moving average of a time-series list of numbers:
movavg n [] = []
movavg n (x:xs) = map (/ n') sums
where
sums = scanl (+) (n' * x) $ zipWith (-) xs (replicate n x ++ xs)
n' = fromIntegral n
What would be the idiomatic version of this in Clojure?
I don't see why a very literal translation of this shouldn't be idiomatic, i.e.:
(defn movavg [n coll]
(when-let [[x & xs] (seq coll)]
(map #(/ % n)
(reductions + (* n x)
(map - xs (concat (repeat n x) xs))))))
Particularly code with a lot of sequence functions has always the potential to be very close to Haskell since they're lazy.
Edit: Shortened code according to Justin Kramer's suggestion.