I originally presented my function as a solution where myTakeWhile returns elements of (x:xs) as a list until it reaches an element which the function argument equates to false. After which another solution was presented, which is below.
myTakeWhile :: (a -> Bool) -> [a] -> [a]
myTakeWhile p [] = []
myTakeWhile p (x:xs) = if p x then x : myTakeWhile p xs else []
myTakeWhile :: (a -> Bool) -> [a] -> [a]
myTakeWhile p (x:xs) = foldr (\x acc -> if p x then x : acc else []) [] (x:xs)
I'm having real trouble running through the fold step by step in my head, especially the counter-intuition of a right fold starting from the left side of the list in the tests I have tried below.
*Assignment1a> myTakeWhile (\x -> x `mod` 2 == 0) [1, 2, 3, 4, 5]
[]
*Assignment1a> myTakeWhile (\x -> x `mod` 2 == 0) [8, 10, 12, 1, 2, 3, 4, 5]
[8,10,12]
Fundamentally I somewhat understand how a fold works through looking at lecture notes. However the fold in context is confusing me, even with the currying removed! How do I go about understanding this fold step by step?
let's do it step by step with the example [8,10,12,1,2].
I assume you understand that you can think about a right-fold foldr f a xs by replacing : with `f` and [] with a in xs:
with f = \x acc -> if even x then x:acc else []:
myTakeWhile even [8,10,12,1,2]
= foldr f [] [8,10,12,1,2]
= foldr f [] (8:10:12:1:2:[])
{ replace the : with `f` and [] with [] }
= 8 `f` (10 `f` (12 `f` (1 `f` (2 `f` []))))
{ 2 is even so f 2 [] = 2:[] }
= 8 `f` (10 `f` (12 `f` (1 `f` (2:[]))))
{ 2:[] = [2] }
= 8 `f` (10 `f` (12 `f` (1 `f` [2])))
{ 1 is odd so f 1 [2] is [] }
= 8 `f` (10 `f` (12 `f` ([])))
{ ([]) = [] }
= 8 `f` (10 `f` (12 `f` []))
{ 12 is even so f 12 [] = 12:[] }
= 8 `f` (10 `f` (12:[]))
{ 12:[] = [12] }
= 8 `f` (10 `f` [12])
{ 10 is odd so f 10 [12] = 10:12 }
= 8 `f` (10:[12])
{ 10:[12] = [10,12] }
= 8 `f` [10,12]
{ 8 is odd so f 8 [10,12] is 8:[10,12] }
= 8:[10,12]
{ 8:[10,12] = [8,10,12] }
= [8,10,12]
how does foldr work
to see why foldr does the replacement you just have to remember the definition:
foldr _ a [] = a
foldr f a (x:xs) = f x (foldr f a xs) = x `f` (foldr f a xs)
the trick is to think recursive (with induction):
foldr f a []
{ definition }
a
foldr f b (b:bs)
{ definition foldr x <- b; xs <- bs }
= b `f` (foldr f a bs)
{ induction/recursion }
= b `f` { bs with : replaced by `f` and [] by a }
expanded example
foldr f a [b1,b2]
{ [b1,b2] = b1:b2:[] }
= foldr f a (b1:b2:[])
{ definition foldr x <- b1; xs <- b2:[]}
= b1 `f` (foldr f a (b2:[]))
{ definition foldr x <- b2; xs <- []}
= b1 `f` (b2 `f` (foldr f a []))
{ definition foldr empty case }
= b1 `f`(b2 `f` a)
Related
I have a function prefixes that, given [1, 2, 3], returns the prefixes [[1], [1, 2], [1, 2, 3]]. It is defined as follows:
prefixes :: Num a => [a] -> [[a]]
prefixes = foldr (\x acc -> [x] : (map ((:) x) acc)) []
I have spent nearly two days trying to understand why this works. When I debug this in my head, I imagine this for prefixes [1, 2, 3]:
foldr call|__________________________________________________________________________
1 | [1] : (map ((:) 1) [])
|
| where x = 1 and acc = []
| returns acc = [[1]]
|
2 | [2] : (map ((:) 2) [[1]])
|
| where x = 2 and acc = [[1]]
| and (map ((:) 2) [[1]])
| returns acc = [[1, 2]]
| and [2] : [[1, 2]]
| returns [[2], [1, 2]]
|
3 | [3] : (map ((:) 3) [[2], [1, 2]])
|
| where x = 3 and acc = [[2], [1, 2]]
| and (map ((:) 3) [[2], [1, 2]])
| returns acc = [[2, 3], [1, 2, 3]]
| and [3] : [[2, 3], [1, 2, 3]]
| returns [[3], [2, 3], [1, 2, 3]]
|
And then the function terminates and returns [[3], [2, 3], [1, 2, 3]]. But obviously that is not happening. It returns [[1], [1, 2], [1, 2, 3]].
In Ghci, I find this:
Stopped in Main.prefixes, ex.hs:21:20-63
_result :: [a] -> [[a]] = _
[ex.hs:21:20-63] *Main> :step
Stopped in Main.prefixes, ex.hs:21:37-59
_result :: [[Integer]] = _
acc :: [[Integer]] = _
x :: Integer = 1
[ex.hs:21:37-59] *Main> :step
[[1]
Stopped in Main.prefixes, ex.hs:21:44-58
_result :: [[Integer]] = _
acc :: [[Integer]] = _
x :: Integer = 1
[ex.hs:21:44-58] *Main> :step
Stopped in Main.prefixes, ex.hs:21:37-59
_result :: [[Integer]] = _
acc :: [[Integer]] = _
x :: Integer = 2
[ex.hs:21:37-59] *Main> :step
,
Stopped in Main.prefixes, ex.hs:21:49-53
_result :: [Integer] -> [Integer] = _
x :: Integer = 1
[ex.hs:21:49-53] *Main> :step
[1,2]
Stopped in Main.prefixes, ex.hs:21:44-58
_result :: [[Integer]] = _
acc :: [[Integer]] = _
x :: Integer = 2
[ex.hs:21:44-58] *Main> :step
Stopped in Main.prefixes, ex.hs:21:37-59
_result :: [[Integer]] = _
acc :: [[Integer]] = _
x :: Integer = 3
[ex.hs:21:37-59] *Main> :step
,
[1Stopped in Main.prefixes, ex.hs:21:49-53
_result :: [Integer] -> [Integer] = _
x :: Integer = 2
[ex.hs:21:49-53] *Main> :step
,2,3]
Stopped in Main.prefixes, ex.hs:21:44-58
_result :: [[Integer]] = _
acc :: [[Integer]] = _
x :: Integer = 3
[ex.hs:21:44-58] *Main> :step
]
Which I interpret as:
__lines___|__________________________________________________________________________
21:37-59 | [1] : (map ((:) 1) acc) -> [[1]
|
|
|
21:44-58 | (map ((:) 1) acc) -> does nothing, as acc = []
|
|
|
21:37-59 | [2] : (map ((:) 2) acc) -> ,
|
|
|
21:49-53 | ((:) 1) -> [1, 2]
|
|
|
21:44-58 | (map ((:) 2) acc) -> outputs nothing
|
|
|
21:37-59 | [3] : (map ((:) 3) acc) -> ,[1
|
|
|
21:49-53 | ((:) 2) -> , 2, 3]
|
|
21:44-58 | (map ((:) 3) acc) -> ]
|
Printing [[1], [1, 2], [1, 2, 3]]. Could someone explain why, when lines 49-53 are evaluated, x is the x value from the previous foldr invocation?
I know that (map ((:) x) acc) can be expanded to (foldr ((:) . ((:) x)) [] acc), as map f = foldr ((:) . f) []. So I rewrote the function into the following
prefixesSolution :: Num a => [a] -> [[a]]
prefixesSolution = foldr (\x acc -> [x] : (foldr ((:) . ((:) x)) [] acc)) []
And this works as well. Now, the lambda passed to the second foldr ((:) . ((:) x)) I would imagine could be refactored as (\ element accumulator -> (element:accumulator) . ((element:accumulator) x)). But this does not work: Couldn't match expected type ‘a -> a0 -> b0’ with actual type ‘[[a]]’. All this I have done in order to pinpoint exactly what is happening.
I also do not understand the function passed to map ((:) x).
I apologize for how convoluted this post is. At this point I don't even know what I don't know. If someone could clearly walk me through this function I would be so so grateful.
foldr accumulates from the end of the list.
Initially acc = [] (using the second argument of foldr).
Starting from the end, we apply the given function \x acc -> [x] : (map ((:) x) acc) with x = 3:
[3] : map (3 :) []
= [[3]]
With acc = [[3]], add the preceding element, x = 2:
[2] : map (2 :) [[3]]
= [[2], [2,3]]
With acc = [[2], [2,3]], add the preceding element, x = 1:
[1] : map (1 :) [[2], [2,3]]
= [[1], [1,2], [1,2,3]]
You can also still evaluate foldr "left to right", but in that case, remember that acc gets instantiated with "the next recursive call".
foldr f b (x : xs) = f x (foldr f b xs)
prefixes [1,2,3]
= [1] : map (1 :) (prefixes [2,3]) -- acc = prefixes [2,3], the next recursive call
= [1] : map (1 :) ([2] : map (2 :) (prefixes [3]))
...
Starting with the question about the function passed to map:
In Haskell, all operators are also functions. By itself, : is the list construction ("cons") operator:
1 : [2,3] -- > [1,2,3]
If you put parentheses around it, it becomes a prefix function instead of an infix operator:
(:) 1 [2,3] -- > [1,2,3]
When you remember that Haskell function application is curried, then you can see that (:) 1 is necessarily a function that prepends 1 to a list:
f = (:) 1
f [2,3] -- > [1,2,3]
So the function passed to map is one that takes a list as its argument and prepends x (the current item from the foldr) to that list.
The surrounding function prepends [x] to the result of that map, growing the list.
Next let's talk about foldr itself. It may help to think of the list [1,2,3] as the sequence of cons calls required to create it. In tree form that looks like this:
(:)
1 (:)
2 (:)
3 []
And in Haskell you could write it like this:
(:) 1 ( (:) 2 ( (:) 3 [] ) )
Given the above, what the call foldr func init [1,2,3] does is replace the final [] with the init value and all the (:)s with the supplied func. So the final result is the same as the result of this expression, which you can think of as an expansion of the foldr version:
func 1 ( func 2 ( func 3 init ) )
That is, foldr first calls the func on 3 (which becomes x) and [] (which becomes acc). (Technically, it calls the function on 3, and the result of that call is another function that it then calls on [], but that's just how function application works in Haskell; the difference is not important to this particular discussion.) Then it calls the func on 2 and the result of the first call, and then it calls it on 1 and the result of the second call.
As we established above, the func first does a map ((:) 3) [] - returning [], since mapping anything across the empty list just returns the empty list - and prepends [3] to the result, giving [[3]].
Then it calls the func on 2 and [[3]]. The map returns [[2,3]], to which it prepends [2], yielding [[2],[2,3]].
Finally it calls the func on 1 and [[2],[2,3]]. The map returns [[1,2],[1,2,3]] and the func prepends [1] to it, yielding the final answer [[1],[1,2],[1,2,3]].
When evaluating something like prefixes [1,2,3] by hand, you should try to be very careful in writing out each step of the evaluation.
I would look at it like this:
Before we start, I suggest a couple of steps of preparation. I'll also give variables fresh names as we go, to hopefully make things more clear.
It will help to write the pattern matches as case expressions, so we will do this next.
We can observe that foldr can be written as
foldr f z list =
case list of
[] -> z
(y:ys) -> f y (foldr f z ys)
I'll skip over some of the details of specific map applications later on and focus more on the foldr steps. If this is unclear, I can expand on that more.
Now that we've got that taken care of, we can evaluate. I'm not going to focus so much on the evaluation order, since this will not affect the final result. This will let me simplify a couple of things. As a result, you shouldn't necessarily assume this is exactly what the computer is doing (even though the result is the same here, it could have differences in terms of memory efficiency, time efficiency and possibly strictness properties).
prefixes [1,2,3]
==> {definition of prefixes}
foldr (\x acc -> [x] : (map ((:) x) acc)) [] [1,2,3]
==> {definition of foldr}
let f = \x acc -> [x] : (map ((:) x) acc)
in
case [1,2,3] of
[] -> []
(y:ys) -> f y (foldr f [] ys)
==> {reduce case match on known value}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (foldr f [] [2,3])
==> {definition of foldr}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (case [2,3] of
[] -> []
(y:ys) -> f y (foldr f [] ys))
==> {reduce case match on known value}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (f 2 (foldr f [] [3]))
==> {definition of foldr}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (f 2 (case [3] of
[] -> []
(y:ys) -> f y (foldr f [] ys)))
==> {reduce case match on known value}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (f 2 (f 3 (foldr f [] [])))
==> {definition of foldr}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (f 2 (f 3 (case [] of
[] -> []
(y:ys) -> f y (foldr f [] ys))))
==> {reduce case match on known value}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (f 2 (f 3 []))
==> {apply f}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (f 2 ([3] : map ((:) 3) []))
==> {apply map}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (f 2 ([3] : []))
==> {list sugar}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (f 2 [[3]])
==> {apply f}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 ([2] : map ((:) 2) [[3]])
==> {apply map}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 ([2] : [((:) 2) [3]])
==> {list sugar}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 [[2], [2,3]]
==> {apply f}
[1] : map ((:) 1) [[2], [2,3]]
==> {apply map}
[1] : [((:) 1) [2], ((:) 1) [2,3]]
==> {list sugar}
[1] : [[1,2], [1,2,3]]
==> {list sugar}
[[1], [1,2], [1,2,3]]
This is the general process can be used to understand the result obtained from evaluating expressions. Note that every step is a valid Haskell expression that behaves identically to the original expression. Essentially, I just expanded definitions, reduced case expressions when the case is matching on a (:) ... ... or a [], applied functions (using beta-reduction) and introduced some syntactic sugar for lists to make things a bit easier to read in parts. Those kinds of steps already cover a significant portion of the tools you need to reduce most Haskell expressions by hand.
A very similar process can also be used for equational reasoning, which can be used as a systematic technique to optimize Haskell programs. It works by replacing expressions with other expressions that always give the same result but could have different efficiency characteristics. Essentially anything written by Richard Bird will provide examples of equational reasoning, among others.
Since I am new in "innermost, outermost" I have problem with understanding the leftmost outermost style.
I would like to understand the reduction processes for the list [5,2,1]
foldl :: ( b -> a -> b ) -> b -> [ a ] -> b
foldl _ e [] = e
foldl f e (x:xs) = foldl f (f e x) xs
foldl (\acc x -> acc ++ [negate x]) [] [5,2,1]
You can inline the definition to get a better understanding of what is going on.
foldl (\acc x -> acc ++ [negate x]) [] [5,2,1]
-- using foldl f e (x:xs) = foldl f (f e x) xs
-- with f = (\acc x -> acc ++ [negate x])
-- e = []
-- x = 5
-- xs = [2,1]
-- replace line 1 with foldl f (f e x) xs
foldl f (f [] 5) [2,1]
foldl f (f (f [] 5) 2) [1]
foldl f (f (f (f [] 5) 2) 1) []
-- using foldl _ e [] = e
f (f (f [] 5) 2) 1
-- in infix style (f [] 5 == [] `f` 5)
(([] `f` 5) `f` 2) `f` 1
In general
foldl (+) 0 [a, b, c] == ((0 + a) + b) + c
foldr (+) 0 [a, b, c] == a + (b + (c + 0))
I have the following function and should write it with foldr.
f [] = []
f (x:xs)
| x == 0 = case f xs of
[] -> []
ys#(y:_) -> if y == 0 then ys else x : ys
| otherwise = x : f xs
This function basically removes all trailing 0's, but keeps at most one leading 0.
For example:
f [1,2,0,0] = [1,2]
f [1,2,0,1] = [1,2,0,1]
f [0,0,1,0,0,3] = [0,1,0,3]
I have foldr op z lst, but don't know what op can be. z should be [].
Example I traced:
foldr op [] [0,0,1,0,0,3]
-> 0 op (0 op (1 op (0 op (0 op (3 op []))))
|-- [3] ---|
|---[0,3] ------|
|-----[0,3]-----------|
|-----[1,0,3]---------------|
|-----[0,1,0,3]-------------------|
|-----[0,1,0,3]-------------------------|
How about
f = fst . foldr (\ x (xs', y) -> if y && x == 0 then (xs', x==0) else (x:xs', x==0 )) ([], True)
in this case, op returns a tuple of list and Bool, Bool is for tracking whether the accumulated list started with 0. At the end, we use fst to discard the Bool. We have to use ([], True) as the initial value, to handle the trailing zero case.
Having a hard time understanding fold... Is the expansion correct ? Also would appreciate any links, or analogies that would make fold more digestible.
foldMap :: (a -> b) -> [a] -> [b]
foldMap f [] = []
foldMap f xs = foldr (\x ys -> (f x) : ys) [] xs
b = (\x ys -> (f x):ys)
foldMap (*2) [1,2,3]
= b 1 (b 2 (foldr b [] 3))
= b 1 (b 2 (b 3 ( b [] [])))
= b 1 (b 2 ((*2 3) : []))
= b 1 ((*2 2) : (6 :[]))
= (* 2 1) : (4 : (6 : []))
= 2 : (4 : (6 : []))
First, let's not use the name foldMap since that's already a standard function different from map. If you want to re-implement an existing function with the same or similar semantics, convention is to give it the same name but either in a separate module, or with a prime ' appended to the name. Also, we can omit the empty-list case, since you can just pass that to the fold just as well:
map' :: (a -> b) -> [a] -> [b]
map' f xs = foldr (\x ys -> f x : ys) [] xs
Now if you want to evaluate this function by hand, first just use the definition without inserting anything more:
map' (*2) [1,2,3,4]
≡ let f = (*2)
xs = [1,2,3,4]
in foldr (\x ys -> (f x) : ys) [] xs
≡ foldr (\x ys -> (*2) x : ys) [] [1,2,3,4]
Now just prettify a bit:
≡ foldr (\x ys -> x*2 : ys) [] [1,2,3,4]
Now to evaluate this through, you also need the definition of foldr. It's actually a bit different in GHC, but effectively
foldr _ z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
So with your example
...
≡ foldr (\x ys -> x*2 : ys) [] (1:[2,3,4])
≡ (\x ys -> x*2 : ys) 1 (foldr (\x ys -> x*2 : ys) [] [2,3,4])
Now we can perform a β-reduction:
≡ 1*2 : foldr (\x ys -> x*2 : ys) [] [2,3,4]
≡ 2 : foldr (\x ys -> x*2 : ys) [] [2,3,4]
...and repeat for the recursion.
foldr defines a family of equations,
foldr g n [] = n
foldr g n [x] = g x (foldr g n []) = g x n
foldr g n [x,y] = g x (foldr g n [y]) = g x (g y n)
foldr g n [x,y,z] = g x (foldr g n [y,z]) = g x (g y (g z n))
----- r ---------
and so on. g is a reducer function,
g x r = ....
accepting as x an element of the input list, and as r the result of recursively processing the rest of the input list (as can be seen in the equations).
map, on the other hand, defines a family of equations
map f [] = []
map f [x] = [f x] = (:) (f x) [] = ((:) . f) x []
map f [x,y] = [f x, f y] = ((:) . f) x (((:) . f) y [])
map f [x,y,z] = [f x, f y, f z] = ((:) . f) x (((:) . f) y (((:) . f) z []))
= (:) (f x) ( (:) (f y) ( (:) (f z) []))
The two families simply exactly match with
g = ((:) . f) = (\x -> (:) (f x)) = (\x r -> f x : r)
and n = [], and thus
foldr ((:) . f) [] xs == map f xs
We can prove this rigorously by mathematical induction on the input list's length, following the defining laws of foldr,
foldr g n [] = []
foldr g n (x:xs) = g x (foldr g n xs)
which are the basis for the equations at the top of this post.
Modern Haskell has Fodable type class with its basic fold following the laws of
fold(<>,n) [] = n
fold(<>,n) (xs ++ ys) = fold(<>,n) xs <> fold(<>,n) ys
and the map is naturally defined in its terms as
map f xs = foldMap (\x -> [f x]) xs
turning [x, y, z, ...] into [f x] ++ [f y] ++ [f z] ++ ..., since for lists (<>) == (++). This follows from the equivalence
f x : ys == [f x] ++ ys
This also lets us define filter along the same lines easily, as
filter p xs = foldMap (\x -> [x | p x]) xs
To your specific question, the expansion is correct, except that (*2 x) should be written as ((*2) x), which is the same as (x * 2). (* 2 x) is not a valid Haskell (though valid Lisp :) ).
Functions like (*2) are known as "operator sections" -- the missing argument goes into the empty slot: (* 2) 3 = (3 * 2) = (3 *) 2 = (*) 3 2.
You also asked for some links: see e.g. this, this and this.
Let's say I have the following function:
sumAll :: [(Int,Int)] -> Int
sumAll xs = foldr (+) 0 (map f xs)
where f (x,y) = x+y
The result of sumAll [(1,1),(2,2),(3,3)] will be 12.
What I don't understand is where the (x,y) values are coming from. Well, I know they come from the xs variable but I don't understand how. I mean, doing the code above directly without the where keyword, it would be something like this:
sumAll xs = foldr (+) 0 (map (\(x,y) -> x+y) xs)
And I can't understand, in the top code, how does the f variable and (x,y) variables represent the (\(x,y) -> x+y) lambda expression.
Hopefully this will help. The key is that f is applied to the elements of the list, which are pairs.
sumAll [(1,1),(2,2),(3,3)]
-- definition of sumAll
= foldr (+) 0 (map f [(1,1),(2,2),(3,3)])
-- application of map
= foldr (+) 0 (f (1,1) : map f [(2,2),(3,3)])
-- application of foldr
= 0 + foldr (+) (f (1,1)) (map f [(2,2),(3,3)])
-- application of map
= 0 + foldr (+) (f (1,1)) (f (2,2) : map f [(3,3)])
-- application of foldr
= 0 + (f (1,1) + foldr (+) (f (2,2)) (map f [(3,3)]))
-- application of f
= 0 + (2 + foldr (+) (f (2,2)) (map f [(3,3)]))
-- application of map
= 0 + (2 + foldr (+) (f (2,2)) (f (3,3) : map f []))
-- application of foldr
= 0 + (2 + (f (2,2) + foldr (+) (f (3,3)) (map f [])))
-- application of f
= 0 + (2 + (4 + foldr (+) (f (3,3)) (map f [])))
-- application of map
= 0 + (2 + (4 + foldr (+) (f (3,3)) []))
-- application of foldr
= 0 + (2 + (4 + f (3,3)))
-- application of f
= 0 + (2 + (4 + 6))
= 0 + (2 + 10)
= 0 + 12
= 12
In Haskell, functions are first class datatypes.
This means you can pass functions around like other types of data such as integers and strings.
In your code above you declare 'f' to be a function, which takes in one argumenta (a tuple of two values (x,y)) and returns the result of (x + y).
foldr is another function which takes in 3 arguments, a binary function (in this case +) a starting value (0) and an array of values to iterator over.
In short 'where f (x,y) = x + y' is just scoped shorthand for
sumAll :: [(Int,Int)] -> Int
sumAll xs = foldr (+) 0 (map myFunctionF xs)
myFunctionF :: (Int,Int) -> Int
myFunctionF (x,y) = x + y
Edit: If your unsure about how foldr works, check out Haskell Reference Zvon
Below is an example implementation of foldl / map.
foldl :: (a -> b -> b) -> b -> [a] -> b
foldl _ x [] = x
foldl fx (y:ys) = foldl f (f y x) ys
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = (f x) : (map f xs)
Not an answer, but I thought I should point out that your function f:
f (x, y) = x + y
can be expressed as
f = uncurry (+)