Given the following function:
let rec foo l1 l2 =
match (l1,l2) with
([],ys) -> ys
| (x::xs,ys) -> foo xs (x::ys))
Prove the following property:
foo (foo xs ys) zs = foo ys (xs#zs)
So far, I have completed the base case and inductive case, but have no idea how to start the proof:
Base Case:
foo (foo [] ys) zs = foo ys ([]#zs)
foo ys zs = foo ys zs
Inductive Case:
foo (foo (x::xs) ys) zs = foo ys ((x::xs)#zs)
An outline of the kind of proof you are asking about follows. I included the base case. The inductive case is left for you. Make sure you use the assumption mentioned in the outline somewhere in the inductive case to complete it. I am using = for "equality" and => for evaluation. I don't know what relations are available in your context, what you are allowed to assume about evaluation and equality, or whether you are allowed to use lemmas about # or have an abstract definition available. So, you will likely have to modify this.
Proof by induction on the structure of xs.
Case: xs = []:
foo (foo xs ys) zs
= foo (foo [] ys) zs (* structure of xs *)
=> foo (match ([], ys) with ([], ys) -> ys | (* ... *)
(* def. of foo, substitution *)
=> foo (ys) zs (* eval. of match *)
= foo ys zs (* drop parentheses *)
= foo ys ([] # zs) (* abstract def. of # *)
= foo ys (xs # zs) (* structure of xs *)
Case: xs = x::xs':
Here, suppose that, for all ys, zs, foo (foo xs' ys) zs = foo ys (xs' # zs). (This is the so-called inductive hypothesis.)
foo (foo xs ys) zs
= foo (foo (x::xs') ys) zs (* structure of xs *)
= foo (match (x::xs', ys) with (* ... *) | (x::xs', ys) -> (* ... *)
(* def. of foo, substitution *)
=> foo (foo xs' (x::ys)) zs (* eval. of match *)
(* for you *)
= foo ys ((x::xs') # zs) (* by some argument from you *)
= foo ys (xs # zs) (* structure of xs *)
As you can see, the proof starts by picking a value to do structural induction on (you already picked xs in your question). Then, the proof splits into cases according to all the possible ways xs might be constructed. Since xs is presumably a list (this is why type information is important), there are only two kinds of things it could be: it might be [], or it might be x::xs' for some value x and list xs'. This results in the base case and the inductive case, respectively. In both cases, we have to prove the same original property, but we know extra information about what xs looks like (i.e., the "structure" of xs).
For each case, you use the structure to figure out the statement you want to get to (approximately correct in your original question). Then, you try to simply go from the expression on the left side of the statement to the expression on the right side, using the rules of evaluation, identity, and any lemmas you have available. In the inductive case, you have an additional fact you can use about xs' (the "inductive hypothesis"). This "direct" approach isn't going to work in all (perhaps most) cases at the level of research, but it works fine for this exercise.
The actual statements proved in the cases are
If xs = [], foo (foo xs ys) zs = foo ys (xs # zs); and
If xs = x::xs' and foo (foo xs' ys) zs = foo ys (xs' # zs), then foo (foo xs ys) zs = foo ys (xs # zs).
ys and zs are implicitly universally quantified.
Claim: forall xs, ys, zs, foo (foo xs ys) zs = foo ys (xs#zs)
Proof: By induction on xs
P(xs): forall ys, zs, foo (foo xs ys) zs = foo ys (xs#zs)
P([]): forall ys, zs, foo (foo [] ys) zs = foo ys ([]#zs)
LHS:
= foo (foo [] ys) zs
= foo (ys) zs
= foo ys zs
RHS:
= foo ys ([]#zs)
= foo ys zs {eval #}
= LHS
IC: xs=h::t
IH: P(t): forall ys, zs, foo (foo t ys) zs = foo ys (t#zs)
Show: P(h::t): forall ys, zs, foo (foo h::t ys) zs = foo ys (h::t#zs)
LHS:
= foo (foo h::t ys) zs
= foo (foo t (h::ys)) zs
= foo (h::ys) (t#zs) {IH with ys:=h::ys}
= foo ys (h::t#zs) {eval of foo}
= RHS
RHS:
= foo ys (h::t#zs)
= LHS
QED
Related
I don't quite understand why given two list of lists xss :: [[a]] and yss :: [[a]]
liftA2 (++) xss yss
is equivalent to
[xs ++ ys | xs <- xss, ys <- yss]
The reason is right here in the source code.
instance Applicative [] where
pure x = [x]
fs <*> xs = [f x | f <- fs, x <- xs]
liftA2 f xs ys = [f x y | x <- xs, y <- ys]
The liftA2 definition is an optimization, and we could also do it manually with the default definition of liftA2:
liftA2 f x y = f <$> x <*> y
So
liftA2 (++) xs ys
= (++) <$> xs <*> ys
= fmap (++) xs <*> ys -- definition of <$>
= [ f y | f <- fmap (++) xs, y <- ys ] -- definition of <*> above
= [ (++) x y | x <- xs, y <- ys ] -- some law about fmap/bind
= [ x ++ y | x <- xs, y <- ys ]
There you have it.
"Some law about fmap/bind" is this one:
fmap f x >>= t = x >>= t . f
which applies if you understand how list comprehensions are desugared. The proof is:
fmap f x >>= t
= x >>= pure . f >>= t -- fmap = liftM coherence
= x >>= (\y -> pure (f y) >>= t) -- definition of (.)
= x >>= (\y -> t (f y)) -- left unit monad law
= x >>= t . f -- definition of (.)
This is exactly the reason why i don't like using liftA<2..n> type of functions. They are an abstraction over the monad abstraction. It's just so because applicative is introduced after monads just to simplify to context of monads those contain functional values (functions).
Basically liftA2 (++) xs ys is (++) <$> xs <*> ys which makes more sense since it involves functor operator fmap in it's inline form <$>. Once you undertand the mechanics of the latter liftA2 starts to make sense.
fmap just applies the (++) function to the elements of the xs list (assume that xs is [[1,2],[3,4]]) and turns it into an applicative list (a list that contains functions) such as;
[([1,2] ++), ([3,4] ++)] :: Num a => [[a] -> [a]]
and the applicative operator <*> is now eligible to apply these functions in our list to another list which contains some other lists such as say, [[1,2],[3,4]].
At this very moment we have to know how exactly lists are handled monadically. Lists are undeterministic data types. So every single element of the first list has to be applied to the every single element of the second list. So
[([1,2] ++), ([3,4] ++)] <*> [[1,2],[3,4]]
turns out to be
[[1,2,1,2],[1,2,3,4],[3,4,1,2],[3,4,3,4]]
All in all liftA2 (++) just lifts the simple (++) function to the list monad. Simply saying, concatenate inner lists with each other monadically.
Having said that the list comprehensions version of this is a joke in Haskell. It is redundant and should be avoided in my honest opinion. It just takes a whole monad abstraction down to list level only whereas monadical approaches hold for all data types according to their appropriate monad instances.
I wrote a filter function:
f :: (a -> Bool) -> [a] -> [a]
f p xs = case xs of
[] -> []
x : xs' -> if p x
then x : f p xs'
else f p xs'
To understand bind, I want to implement this using bind.
What I was thinking about:
f p xs = xs >>= (\x xs -> if p x then x : f p xs else f p xs)
But I get this error:
* Couldn't match expected type `[a]' with actual type `[a] -> [a]'
* The lambda expression `\ x xs -> ...' has two arguments,
but its type `a -> [a]' has only one
In the second argument of `(>>=)', namely
`(\ x xs -> if p x then x : f p xs else f p xs)'
In the expression:
xs >>= (\ x xs -> if p x then x : f p xs else f p xs)
* Relevant bindings include
xs :: [a] (bound at <interactive>:104:5)
p :: a -> Bool (bound at <interactive>:104:3)
f :: (a -> Bool) -> [a] -> [a] (bound at <interactive>:104:1)
Successfully did it using foldr:
f p xs = foldr (\x xs -> if p x then x : f p xs else f p xs) [] xs
What's going wrong?
To understand bind, i want to implement this as bind.
There is no bind here. The bind is added in case of a do expression. The above is not a do-expression, so there is no bind here.
You can however write this with bind, like:
f p xs = xs >>= \x -> if p x then [x] else []
but this is not a literal mapping of the original function, we simply make use of the instance Monad [] implementation here. Nevertheless, your f is just filter :: (a -> Bool) -> [a] -> [a] here.
To understand bind, first implement the no-op:
id_list xs = concat [ [x] | x <- xs ] = [ y | x <- xs, y <- [x ] ]
Now for the filter, augment it as
filter p xs = concat [ [x | p x] | x <- xs ] = [ y | x <- xs, y <- [x | p x] ]
How is this code using bind, you ask? If we're using MonadComprehensions, it does.
The explicit do-notation re-write is straightforward:
id_list xs = do { x <- xs ; y <- [ x ] ; return y }
filter p xs = do { x <- xs ; y <- [ x | p x] ; return y }
And of course, for lists,
[x] == return x
[x | p x] == if p x then return x else mzero
mzero == []
concat == join
This brings us back to an explicitly recursive way to code filter as
filter p [] = []
filter p (x:xs) = [x | p x] ++ filter p xs
With bind, we think in terms of transforming each element of the list individually, into the list of results (none, one, or several) for that one input element. Your foldr-based code breaks this.
So, the code itself is just
filter_bind p xs = xs >>= (\x -> [x | p x])
because we have
xs >>= f == join (fmap f xs)
== concat (map f xs)
== concat [ f x | x <- xs ]
== foldr (++) []
[ f x | x <- xs ]
with the last snippet corresponding to the explicitly recursive definition above.
See also
How does the List monad work in this example?
Haskell Monad - How does Monad on list work?
etc.
I've rewritten the zipWith function using recursion, and now I am trying to rewrite it using list comprehension. I have run into quite a few binding errors and I know that my second line is incorrect. This is the function I have that works like zipWith using recursion:
zipW :: (a -> b -> c) -> [a] -> [b] -> [c]
zipW _ [] _ = []
zipW _ _ [] = []
zipW f (x:xs) (y:ys) = f x y : zipW f xs ys
And this is my attempt to rewrite it as list comprehension:
zipW2 :: (a -> b -> c) -> [a] -> [b] -> [c]
zipW2 f xs ys = [f x y | (x, y) <- zipW2 f xs ys]
I am not sure how to correct the second statement so that it works like zipWith and allows me to choose the operator.
You will need Parallel List Comprehensions extension:
{-# LANGUAGE ParallelListComp #-}
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' f xs ys = [f x y | x <- xs | y <- ys]
The original zipWith has three cases:
when the first list is empty
when the second list is empty
when the neither list is empty
The third case recursively calls zipWith on the tails of the arguments, which does the case analysis again.
In your definition, you only have one case - the list comprehension, so any recursive calls are going to wrap right back to that. And without case analysis, you could loop forever here:
>>> let myZipWith f xs ys = [ f x y | (x,y) <- myZipWith f xs ys ]
>>> myZipWith (,) [] []
^CInterrupted.
Furthermore because you're using f in the recursive call but requiring that the recursive output be a pair, you're placing the implicit requirement that f x y produce a pair:
>>> :t myZipWith
myZipWith :: (t2 -> t3 -> (t2, t3)) -> t -> t1 -> [(t2, t3)]
The solution is to not recurse, but instead to consider each pair directly.
You can use behzad.nouri's solution of enabling the ParallelListComp language extension:
>>> :set -XParallelListComp
>>> let myZipWith f xs ys = [ f x y | x <- xs | y <- ys ]
>>> myZipWith (+) [1,2,4] [0,10,20]
[1,12,24]
ParallelListComp makes the second (and later) vertical pipe characters (|) in a list comprehension legal syntax, stepping through those lists in parallel (zip-like) with earlier lists.
It's good to know how this differs from normal list comprehensions, where you separate each list you draw from with commas. Using commas does nested iteration which is flattened out in the resulting list:
>>> let notZipWith f xs ys = [ f x y | x <- xs, y <- ys ]
>>> notZipWith (+) [1,2,4] [0,10,20]
[1,11,21,2,12,22,4,14,24]
Using the ParallelListComp extension is really just syntatical sugar for the original zipWith, so you may consider it cheating.
You could also just rely on the original zip:
>>> let myZipWith f xs ys = [ f x y | (x,y) <- zip xs ys ]
>>> myZipWith (+) [1,2,4] [0,10,20]
[1,12,24]
But since zip is defined as zipWith (,), that's probably cheating too.
Another way you could go is to use indices:
>>> let myZipWith f xs ys = [ f x y | i <- [0..min (length xs) (length ys) - 1], let x = xs !! i, let y = ys !! i ]
>>> myZipWith (+) [1,2,4] [0,10,20]
[1,12,24]
But this is going to be horrendously inefficient, as !! is a linear-time operation, making myZipWith quadratic, while zipWith is linear:
>>> :set +s
>>> last $ myZipWith (+) (replicate 10000000 1) (replicate 10000000 2)
3
(4.80 secs, 3282337752 bytes)
>>> last $ zipWith (+) (replicate 10000000 1) (replicate 10000000 2)
3
(0.40 secs, 2161935928 bytes)
I'm sure there's other bad ways to create an equivalent to zipWith with a list comprehension, but I'm not terribly convinced that there's a good way, even from the ones above.
I'm trying to make an example function tail recursive.
Here is the original function:
let rec s xs ys =
match (xs, ys) with
|([],[]) -> []
|(xs, []) -> xs
|([], ys) -> ys
|(x::xs,y::ys) -> x::y::s xs ys
Below is my attempt to make it tail recursive:
let sC xs ys =
let rec sCTR xs ys acc =
match (xs, ys) with
|([],[]) -> acc
|(xs, []) -> acc#xs
|([], ys) -> acc#ys
|(x::xs,y::ys) -> sCTR xs ys acc#[x]#[y]
sCTR xs ys []
My issue is, however, that the order of the items are all wrong.
When I input the lists [1;2;3;] [7;8;] in the first function I get the result [1; 7; 2; 8; 3]
But when I input [1;2;3;] [7;8;] in the second function I get [3; 2; 8; 1; 7]
Why is the order wrong? I thought that list1#list2 would result in a new list with the order of list1 elements first and then list2 elements
You've just assumed the wrong precedence for #; what you've got is interpreted as
(sCTR xs ys acc)#[x]#[y]
but what you want is
sCTR xs ys (acc#[x]#[y])
I need to write a function to merge two lists. Exactly like '++' is working.
let x = merge [1,2,3] [3,3,4] -- should output [1,2,3,3,3,4]
How should it be done?
Edit: solution is
merge :: [a] -> [a] -> [a]
merge [] ys = ys
merge (x:xs) ys = x : (merge xs ys)
Maybe something like this.
merge :: (a -> a -> Bool) -> [a] -> [a] -> [a]
merge pred xs [] = xs
merge pred [] ys = ys
merge pred (x:xs) (y:ys) =
case pred x y of
True -> x: merge pred xs (y:ys)
False -> y: merge pred (x:xs) ys
(++) xs ys = merge (\x y -> compare x y == LT) xs ys
Or, if you just need to repeat the functionality of (++), you can look up it's definition with hoogle which eventually leads you to the source code
(++) [] ys = ys
(++) (x:xs) ys = x : xs ++ ys