在Haskell中使用foldl或foldr的map函数

时间:2015-11-20 16:33:13

标签: haskell

我正在编写一个函数my_map,它接受一元函数和一个列表,并返回通过将函数映射到输入列表的所有元素而得到的列表。

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

[1,8,27,64,125]

我试过这样:

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

但是在上面跑之后,我只得到[8,27,64,125]。第一个数字1未显示在输出中。

有人能帮助我吗?

2 个答案:

答案 0 :(得分:4)

您在参数中使用了(x:xs)模式,但是当您应用折叠时,只将其应用于xs部分,这意味着您的第一个元素,即x代表的元素永远不会被处理。您需要将其更改为:

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

由于您使用的是foldr,因此您无需显式处理空列表案例。此外,您不需要以(x:xs)格式指定列表。

最后,我自己的偏好是避免在函数定义中使用相同的名称作为函数输入和任何辅助函数或表达式。这就是为什么我使用xs作为输入列表和{{1} }和y用于传递给lambda的参数。

答案 1 :(得分:0)

"shree.pat18" 说得很对,评论也很有价值。我从中学到了很多。只是让它更好地可见,并解释替代方案......

答案

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

剩余部分 xs 应用于 foldr

要解决此问题,请应用整个列表。这可以通过将 xx@ 放在 (x:xs) 之前来完成。这样,整个列表都绑定到 xx

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

建议改进

注意:foldr 已经可以处理 [] 作为输入。因此,不需要 my_map f [] = []。但是当您将 foldr 应用于 [] 时,不会调用 my_map。要去掉 my_map f [] = [],您需要删除模式匹配,因为 (x:xs) 只匹配包含至少一个元素的列表。

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

答案到此为止。下面的其余内容仅供娱乐。

进一步减少

简单表达式代替 lambda 表达式

如果你想减少 lambda 表达式 (\x xs -> (f x):xs),正如“Aadit M Shah”所建议的那样......

(:) 等于 (\x xs -> x:xs),因为 : 是一个运算符,它的功能是 (:)

. 可用于将函数 f(:) 结合,因此 (\x xs -> (f x):xs) 等于 ((:) . f)

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

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

咖喱

表单的一个函数

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

可以减少到

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

和一个函数的形式

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

可以减少到

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

等等,通过柯里化。

因此,my_map f xx = foldr ((:) . f) [] xx 等于 my_map f = foldr ((:) . f) []

组合与翻转

flip 翻转前两个参数。

例如,以下函数是相等的:

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'))

因此,以下代码也能正常工作。

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

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

但是我们不能像上面那样去掉f,因为表达式flip foldr [] ((:) . f)的形式。

如果我们删除 f ...

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

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

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

因此

f :: a -> a

被传递给

((:) .  )

成为

a -> [a] -> [a]

被传递给

flip foldr []

成为

t a1 -> [a2]

因此,

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

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

效果很好。