简体   繁体   中英

map function using foldl or foldr in Haskell

I am writing a function my_map which takes a unary function and a list and returns the list resulting from mapping the function over all elements of the input list.

Main> my_map (^3) [1..5]

[1,8,27,64,125]

I tried it like this:

my_map :: (a -> b) -> [a] -> [b]
my_map f [] = []
my_map f (x:xs) = foldr (\x xs -> (f x):xs) [] xs

But after running above, I get only [8,27,64,125] . the first number 1 is not displaying in output.

Can anybody help me?

You are using the (x:xs) pattern in your arguments, but when you apply the fold, you only apply it to the xs part, which means your first element ie the one that x represents never gets processed. You need to change it to this:

my_map :: (a -> b) -> [a] -> [b]
my_map f xs = foldr (\y ys -> (f y):ys) [] xs

Since you are using foldr , you do not need to explicitly handle the empty list case. Moreoever, you do not need to specify the list in (x:xs) format.

Finally, my own preference is to avoid using the same name for function inputs and any helper functions or expressions in the function definition.That is why, I have used xs for the input list and y and ys for the parameters passed to the lambda.

"shree.pat18" is perfectly right, and also the comments are valuable. I learned a lot from that. Just make it better visible, and to explain the alternatives...

Answer

-- The problem is here ....................... vv
my_map f (x:xs) = foldr (\x xs -> (f x):xs) [] xs
--                                             --

The remaining part xs is aplied to foldr .

To fix just this, apply the whole list. This can be done by placing xx@ before (x:xs) . By that, the whole list is bound to xx .

--       vvv ........... see here ............... vv
my_map f xx@(x:xs) = foldr (\x xs -> (f x):xs) [] xx
--       ---                                      --

Recommended impovement

Note: foldr can already deal with [] as input. Hence, my_map f [] = [] is not needed. But foldr would not be called when you apply [] to my_map . To get rid of my_map f [] = [] , you need to remove the pattern matching, because (x:xs) matches only to lists with at least one element.

main :: IO ()
main = print $ my_map (^(3 :: Int)) ([1..5] :: [Integer])

my_map :: (a -> b) -> [a] -> [b]
my_map f xx = foldr (\x xs -> (f x):xs) [] xx

The answer is complete here. The rest below is for pleasure.

Further reductions

Simple expression instead of lambda expression

If you want to reduce the lambda expression (\\x xs -> (fx):xs) , as suggested by "Aadit M Shah"...

(:) is equal to (\\x xs -> x:xs) , because : is an operator and its function is (:)

. can be used to combine the function f with (:) , hence (\\x xs -> (fx):xs) is equal to ((:) . f)

main :: IO ()
main = print $ my_map (^(3 :: Int)) ([] :: [Integer])

my_map :: (a -> b) -> [a] -> [b]
my_map f xx = foldr ((:) . f) [] xx

Currying

A function of the form

--    v        v
f a b c = .... c

can be reduced to

--    v        v
f a b   = ....

and a function of the form

--  v v        v v
f a b c = .... b c

can be reduced to

--  v v        v v
f a     = ....

and so on, by currying.

Hence, my_map f xx = foldr ((:) . f) [] xx equals my_map f = foldr ((:) . f) [] .

Combination and flip

flip flips the first two parameters.

Example, the following functions are equal:

f' a b c = (\c' b' a' -> ((a' - b') / c')) b a c

f'' a b c = flip (\c' b' a' -> ((a' - b') / c')) a b c

f''' = flip (\c' b' a' -> ((a' - b') / c'))

Hence, the following code works as well.

main :: IO ()
main = print $ my_map (^(3 :: Int)) ([1..5] :: [Integer])

my_map :: (a -> b) -> [a] -> [b]
my_map f = flip foldr [] ((:) . f)

But we can not get rid of f as above, because of the form in the expression flip foldr [] ((:) . f) .

If we remove f ...

`((:) . f)` has type `a -> [a] -> [a]

--     v
`((:) .  )` has type `(a -> a) -> a -> [a] -> [a]`

and

`flip foldr []` has type `Foldable t => (a1 -> [a2] -> [a2]) -> t a1 -> [a2]`

hence

f :: a -> a

is passed to

((:) .  )

becomming

a -> [a] -> [a]

is passed to

flip foldr []

becomming

t a1 -> [a2]

Hence,

main :: IO ()
main = print $ my_map (^(3 :: Int)) ([1..5] :: [Integer])

my_map :: (a -> b) -> [a] -> [b]
my_map = flip foldr [] . ((:) . )

works nicely.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM