I want to write a function that checks if two lists are "almost" equal. The first parameter d is used for precision - the difference between the elements must not exceed d.
For example, nearlyEqual 0.5 [2,5] [2.5, 5.1] equals True, but nearlyEqual 0.1 [2,5] [2.5, 5.1] equals False.
I wrote this but it is not working:
nearlyEqual :: Int -> [Int] -> [Int] -> Bool
nearlyEqual d xs ys = foldr(&&) True $ zipWith (\x y -> abs(x-y)<=d)
What am I missing? Any help would be greatly appreciated!
Not sure if it's a typo, but you're not passing xs and ys to your function.
nearlyEqual d xs ys = foldr(&&) True $ zipWith (\x y -> abs(x-y)<=d)
should be
nearlyEqual d xs ys = foldr(&&) True $ zipWith (\x y -> abs(x-y)<=d) xs ys
at least for it to typecheck.
A clearer implementation would make use of all, which is of type Foldable t => (a -> Bool) -> t a -> Bool, and of the function composition operator (.):
nearlyEqual d xs ys = all ((<= d) . abs) $ zipWith (-) xs ys
where zipWith (-) xs ys is element-wise difference of the two lists, and all verifies that the predicate (<= d) . abs holds for all of the elements of that list; the predicate, given an argument, applies abs to it, and then (<= d) to the result.
Related
What does the x: mean in this Code
Implementation of inits using foldr
inits :: [a] -> [[a]]
inits = foldr ( \ x y -> [] : (map (x:) y) ) [[]]
This is an effect of the infix operator sectioning [Haskell-wiki]:
(2^) (left section) is equivalent to (^) 2, or more verbosely \x -> 2 ^ x
So (x:) is short for (:) x, or \y -> x : y. The "cons" (:) :: a -> [a] -> [a] is a function that takes an element (type a) and a list (type [a]) and constructs a list with the element followed by the elements in the list.
(x:) :: [a] -> [a] is thus a function that takes a list and prepends that list with x.
We can make the fold function "point free" with:
foldr (((:) [] .) . map . (:)) [[]]
It's a function of a single argument that conses x to some list:
(x:) [] => [x]
(x:) [1, 2] => [x, 1, 2]
Here "conses" means "prepends a value to some list". cons is the "canonical" name of a function that does this. So, the : function is the cons function.
I see a post explaining how to append two lists using foldr.
But I don't understand why we have to switch the lists' order.
append xs ys = foldr (\x y -> x:y) ys xs
The first move would be
[y,x] (\x y -> x:y) foldr (\x y -> x:y) ys' xs'
Am I correct? Will the result then put ys in front of xs?
Shouldn't it be
append xs ys = foldr (\x y -> x:y) xs ys
The first move would be
[y,x] (\x y -> x:y) foldr (\x y -> x:y) ys' xs'
That's not a valid Haskell expression, but I think what you mean to express here is that you take one element from each list and then insert them in front somehow. That's not how foldr works - it does not iterate over the elements of the z argument (ys in this case) - in fact that argument doesn't even have to be a list.
Instead foldr f z (x:xs') expands like this:
x `f` foldr f z xs'
and foldr f z [] expands to z.
So in your case, the first step would be:
x : foldr (\x y -> x:y) ys xs'
This will continue until we get to the empty list, in which case ys will be the result. So:
foldr (\x y -> x:y) ys [x1, x2, ..., xn]
= x1 : foldr (\x y -> x:y) ys [x2, ..., xn]
= x1 : x2 : foldr (\x y -> x:y) ys [..., xn]
= ...
= x1 : x2 : ... : xn : foldr (\x y -> x:y) ys []
= x1 : x2 : ... : xn : ys
And from this you can see that the elements of xs are put in front of ys.
The first move wouldn't be that. Check the type of foldr
ghci>:t foldr
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
Just for simplifying let's assume t to be []. In that case, foldr is:
Give me a function f which takes an a and a b and returns a b. Give me an initial element and a list of a. I will produce a b.
So, the way it works is: take the last element of the list and apply f to that element and the initial value producing a b. Take the new last value of the list and apply f to that and the previous result... and so on.
In your case, the initial element is actually a list and that's messy. But check this computation. Keep in mind that [1,2,3] is used as initial value, so we don't "loop" over it
foldr (\x y -> x:y) [1,2,3] [4,5,6]
foldr (\x y -> x:y) 6:[1,2,3] [4,5]
foldr (\x y -> x:y) 5:6:[1,2,3] [4]
foldr (\x y -> x:y) 4:5:6:[1,2,3] []
Hope it helps!
The intuition is that foldr c n list "replaces" every : in list with c and the final [] with n.
To append xs with ys, one needs to "replace" the final [] inside xs with ys. Instead : should be replaced with itself.
Hence, we get foldr (:) ys xs.
I am trying to understand pointfree programming in Haskell and I questions on some examples, because I don't really understand the explanation given when the errors occur.
1) I have a cycle function defined below:
myCycle :: [a] -> [a]
myCycle = foldr (++) [] . repeat
Why does myCycle = foldr (++) [] $ repeat not work?
2) Add every element of a list with 2 then add with another list
sum :: [Int] -> [Int] -> [Int]
sum s = zipWith (+) . map (+ 2) $ s
Why does the function has the same result with sum s = zipWith (+) $ map (+ 2) s and why does sum l1 l2 = zipWith (+) . map (+ 2) $ l1 $ l2 not work
First of all, let's list all types:
foldr :: (a -> b -> b) -> b -> [a] -> b
(++) :: [a] -> [a] -> [a]
[] :: [a]
repeat :: a -> [a]
(.) :: (b -> c) -> (a -> b) -> a -> c
($) :: (a -> b) -> a -> b
foldr (++) :: [a] -> [[a]] -> [a]
foldr (++) [] :: [[a]] -> [a]
Now, as you can see, ($) doesn't change the type at all. It's just so that its fixity makes sure that you can use it instead of parentheses. Let's see how they differ:
($) (foldr (++) []) :: [[a]] -> [a]
(.) (foldr (++) []) :: (b -> [[a]]) -> b -> [a]
Since repeat has type c -> [c], it doesn't work with ($). It sure does with (.), since c ~ [a] works fine.
So always keep in mind that ($) doesn't do anything on its own. It merely changes the precedence/fixity. Also, it sometimes helps if you use prefix notation instead of infix if you try to understand/come to pointfree code:
sum l1 l2 = zipWith (+) (map (+2) l1) l2
= zipWith (+) (map (+2) l1) $ l2
= ($) (zipWith (+) (map (+2) l1)) l2
-- get rid of both ($) and l2:
sum l1 = zipWith (+) (map (+2) l1)
= (zipWith (+)) ((map (+2)) l1)
= f (g l1) -- f = zipWith (+), g = map (+2)
= (f . g) l1
= (zipWith (+) . (map (+2)) l1 -- substitute f and g again
= zipWith (+) . (map (+2) $ l1
-- get rid of $ and l1:
sum = zipWith (+) . map (+2)
If you check the signatures in GHCi you get
Prelude> :t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
Prelude> :t ($)
($) :: (a -> b) -> a -> b
This shows that the dot operator operates on functions while the dollar operator is just a strange version of the normal function application (it allows you to write e.g. f (g (h x)) as f $ g $ h $ x).
In your mycycle example foldr (++) [] has signature [[a]] -> [a] and repeat has a -> [a]. So when typing foldr (++) [] $ repeat Haskell tries to match the function signature a -> [a] with the first argument of the foldr expression which is [[a]], a list of lists. This fails and gives an error. The dot operator actually expects a function and everything is fine.
In your second example, sum s = zipWith (+) . map (+ 2) $ s is equivalent to sum = zipWith (+) . map (+ 2). Type inference regards zipWith (+) as a unary function returning a unary function and is able to match it to the argument expected by the dot operator. So here the functions are first composed and then applied to s. In sum s = zipWith (+) $ map (+ 2) s there is no composition, just application: first map (+ 2) is applied to s and then zipWith (+) is applied to the result.
The point of pointfree programming is to use less function application and more function composition.
myCycle = foldr (++) [] $ repeat is equivalent to myCycle z = (foldr (++) [] $ repeat) z.
(x $ y) z is equal to (x y) z; (x . y) z is equal to x (y z).
The best way to gain insight on these things in haskell is to just manually expand things out, based on their definitions.
(f . g) = \x -> f (g x)
f $ x = f x
So, whenever we see (f . g), we can replace it with \x -> f (g x). and when we see f $ x, we can replace it with f x. let's see where this takes us!
myCycle = foldr (++) [] . repeat
Hm, let's expand out the definition of .:
myCycle = \x -> foldr (++) [] (repeat x)
myCycle x = foldr (++) [] (repeat x)
Sweet, this basically does exactly what we'd want it to do. Concatenate a list of repeating x's.
Now, let's see if you had done $:
myCycle = foldr (++) [] $ repeat
That becomes:
myCycle = foldr (++) [] repeat
That's nice and all, but this doesn't make any sense. the third argument of foldr should be a list, but you gave it a function (repeat). repeat is definitely not a list, so this whole affair is kind of silly.
We can try the same thing here:
sum s = zipWith (+) . map (+ 2) $ s
sum s = (zipWith (+) . map (+ 2)) s
sum s = zipWith (+) (map (+ 2) s) -- (f . g) x = f (g x)
And look at the other formulation:
sum s = zipWith (+) $ map (+ 2) s
sum s = (zipWith (+)) (map (+ 2) s)
sum s = zipWith (+) (map (+ 2) s) -- redundant parentheses
and...they're the same thing!
Let's try seeing what the last one does:
sum l1 l2 = zipWith (+) . map (+ 2) $ l1 $ l2
sum l1 l2 = zipWith (+) . map (+ 2) $ (l1 l2)
Oops...you're trying to do l1 l2, or apply l1 as if it were a function. That doesn't make any sense. l1 is a list, not a function. So, already here you can see why this is nonsense :)
So i have
pair:: [a] -> [b] -> [(a,b)]
pair[] _ = []
pair(x:xs) (y:ys) = (x, y) : prod xs ys
But the result are only like the following:
>> pair [1,2] [3,4]
>> [(1,3),(2,4)]
How can I make this so it pairs like:
[(1,3),(1,4),(2,3),(2,4)]
You can use the list applicative (or monad) instance:
λ> liftA2 (,) [1,2] [3,4]
[(1,3),(1,4),(2,3),(2,4)]
Or, equivalently,
f = do
x <- [1,2]
y <- [3,4]
return (x,y)
You can also use a list comprehension:
[ (x,y) | x <- [1,3], y <- [2,4] ]
Although there is already a much more elegant answer, i think it is worthwhile to show how this would be achieved in a simple straightforward way. If you want to get all pairs, you obviously need to visit every element of one list for an element in the other.
pair :: [a] -> [b] -> [(a, b)]
pair [] _ = []
pair (x:xs) ys = pair' x ys ++ pair xs ys where
pair' :: a -> [b] -> [(a, b)]
pair' _ [] = []
pair' x (y:ys) = (x,y) : pair' x ys
But of course using the pair = liftA2 (,) or [1,3] >>= \x -> [2,4] >>= \y -> (x,y) in its do notation or list comprehension notation is much better. Also ++ isn't what you normally want to do. So maybe you can build the lists as pair' would do, keep them in a list and then concat them.
concat $ map (\x -> map (\y -> (x,y)) ys) xs
So, I'm really frying my brain trying do understand the foldl.foldr composition.
Here is a example:
(foldl.foldr) (+) 1 [[1,2,3],[4,5,6]]
The result is 22, but what's really happening here?
To me it looks like this is what is happening: foldl (+) 1 [6,15].
My doubt is related to the foldr part. Shouldn't it add the 1 to all the sub-lists? Like this: foldr (+) 1 [1,2,3].
In my head the 1 is added just one time, is it right? (probably not, but I want to know how/why!).
I'm very confused (and perhaps making all the confusion, haha).
Thank you!
(foldl.foldr) (+) 1 [[1,2,3],[4,5,6]]
becomes
foldl (foldr (+)) 1 [[1,2,3],[4,5,6]]
So you get
foldl (foldr (+)) (foldr (+) 1 [1,2,3]) [[4,5,6]]
after the first step of foldl, or
foldl (foldr (+)) 7 [[4,5,6]]
if we evaluate the applied foldr (unless the strictness analyser kicks in, it would in reality remain an unevaluated thunk until the foldl has traversed the entire list, but the next expression is more readable with it evaluated), and that becomes
foldl (foldr (+)) (foldr (+) 7 [4,5,6]) []
and finally
foldl (foldr (+)) 22 []
~> 22
Let's examine foldl . foldr. Their types are
foldl :: (a -> b -> a) -> (a -> [b] -> a)
foldr :: (c -> d -> d) -> (d -> [c] -> d)
I intentionally used distinct type variables and I added parentheses so that it becomes more apparent that we view them now as functions of one argument (and their results are functions). Looking at foldl we see that it is a kind of lifting function: Given a function that produces a from a using b, we lift it so that it works on [b] (by repeating the computation). Function foldr is similar, just with arguments reversed.
Now what happens if we apply foldl . foldr? First, let's derive the type: We have to unify the type variables so that the result of foldr matches the argument of foldl. So we have to substitute: a = d, b = [c]:
foldl :: (d -> [c] -> d) -> (d -> [[c]] -> d)
foldr :: (c -> d -> d) -> (d -> [c] -> d)
So we get
foldl . foldr :: (c -> d -> d) -> (d -> [[c]] -> d)
And what is its meaning? First, foldr lifts the argument of type c -> d -> d to work on lists, and reverses its arguments so that we get d -> [c] -> d. Next, foldl lifts this function again to work on [[c]] - lists of [c].
In your case, the operation being lifted (+) is associative, so we don't have care about the order of its application. The double lifting simply creates a function that applies the operation on all the nested elements.
If we use just foldl, the effect is even nicer: We can lift multiple times, like in
foldl . foldl . foldl . foldl
:: (a -> b -> a) -> (a -> [[[[b]]]] -> a)
Actually, (foldl.foldr) f z xs === foldr f z (concat $ reverse xs).
Even if f is an associative operation, the correct sequence of applications matters, as it can have an impact on performance.
We begin with
(foldl.foldr) f z xs
foldl (foldr f) z xs
writing with g = foldr f and [x1,x2,...,xn_1,xn] = xs for a moment, this is
(...((z `g` x1) `g` x2) ... `g` xn)
(`g` xn) ((`g` xn_1) ... ((`g` x1) z) ... )
foldr f z $ concat [xn,xn_1, ..., x1]
foldr f z $ concat $ reverse xs
So in your case the correct reduction sequence is
(foldl.foldr) 1 [[1,2,3],[4,5,6]]
4+(5+(6+( 1+(2+(3+ 1)))))
22
To wit,
Prelude> (foldl.foldr) (:) [] [[1..3],[4..6],[7..8]]
[7,8,4,5,6,1,2,3]
Similarly, (foldl.foldl) f z xs == foldl f z $ concat xs. With snoc a b = a++[b],
Prelude> (foldl.foldl) snoc [] [[1..3],[4..6],[7..8]]
[1,2,3,4,5,6,7,8]
Also, (foldl.foldl.foldl) f z xs == (foldl.foldl) (foldl f) z xs == foldl (foldl f) z $ concat xs == (foldl.foldl) f z $ concat xs == foldl f z $ concat (concat xs), etc.:
Prelude> (foldl.foldl.foldl) snoc [] [[[1..3],[4..6]],[[7..8]]]
[1,2,3,4,5,6,7,8]
Prelude> (foldl.foldr.foldl) snoc [] [[[1..3],[4..6]],[[7..8]]]
[7,8,1,2,3,4,5,6]
Prelude> (foldl.foldl.foldr) (:) [] [[[1..3],[4..6]],[[7..8]]]
[7,8,4,5,6,1,2,3]