Haskell 正在尝试使用“foldr”编写“map”`

Haskell 正在尝试使用“foldr”编写“map”`,haskell,Haskell,我在一次考试中被迫使用foldr 我知道现在有一个简单的方法可以做到这一点,那就是mapf=foldr((:).f)[]。 我的枪是 map' :: (a->b) -> [a] ->[b] map' f [] = [] map' f [x] = foldr f x [] : [] map' f (x:xs) (foldr f x []) ++ map' f xs 我的问题是f需要是a->b->b类型,它是a->b。 我怎样才能强迫它工作?如何更改f以适应此实现?似乎您希望在一

我在一次考试中被迫使用
foldr
我知道现在有一个简单的方法可以做到这一点,那就是
mapf=foldr((:).f)[]
。 我的枪是

map' :: (a->b) -> [a] ->[b]
map' f [] = []
map' f [x] = foldr f x [] : []
map' f (x:xs) (foldr f x []) ++ map' f xs
我的问题是
f
需要是
a->b->b
类型,它是
a->b

我怎样才能强迫它工作?如何更改
f
以适应此实现?

似乎您希望在一个元素上应用foldr,然后递归地将结果相加

似乎没有使用foldr的累积能力

要实现foldr映射,您需要foldr的累积结果类似于映射列表

这就是为什么用foldr fold(:).f(a->[b]->[b])代替f(a->b)

将f应用于元素x之后,您将希望将其作为累积结果(:)(f x)[]=>(f x):[]=>[f x]

foldr ((:).f) [] [x1,x2,x3] = (f x1):(f x2):(f x3):[] = [f x1, f x2, f x3] = map f [x1,x2,x3].
编辑:

无foldr的递归

map' f [] = []
map' f [x] = [f x]
map' f (x:xs) = map' f [x] ++ map' f xs
因此,如果您真的想同时使用递归和foldr:

map' :: (a->b) -> [a] ->[b]
map' f [] = []
map' f [x] = foldr ((:).f) [] [x]
map' f (x:xs) = (foldr ((:).f) [] [x]) ++ map' f xs

f :: a -> b
(:).f :: a -> [b] -> [b]

那么每次foldr将只应用于一个元素。它就像[f x]。

似乎您希望对一个元素应用foldr,然后递归地将结果相加

似乎没有使用foldr的累积能力

要实现foldr映射,您需要foldr的累积结果类似于映射列表

这就是为什么用foldr fold(:).f(a->[b]->[b])代替f(a->b)

将f应用于元素x之后,您将希望将其作为累积结果(:)(f x)[]=>(f x):[]=>[f x]

foldr ((:).f) [] [x1,x2,x3] = (f x1):(f x2):(f x3):[] = [f x1, f x2, f x3] = map f [x1,x2,x3].
编辑:

无foldr的递归

map' f [] = []
map' f [x] = [f x]
map' f (x:xs) = map' f [x] ++ map' f xs
因此,如果您真的想同时使用递归和foldr:

map' :: (a->b) -> [a] ->[b]
map' f [] = []
map' f [x] = foldr ((:).f) [] [x]
map' f (x:xs) = (foldr ((:).f) [] [x]) ++ map' f xs

f :: a -> b
(:).f :: a -> [b] -> [b]

那么每次foldr将只应用于一个元素。它与[f x]类似。

foldr
是一个非常通用的递归列表处理函数,因为它反映了它自身的列表类型结构:

data [a]        -- a list is either
  = a : [a]     -- a head element, plus a tail list
  | []          -- or an empty list

foldr :: (a -> b -> b)    -- given a handler for a head element and the
                          --   result of processing the tail
      -> b                -- and a "handler" for an empty list
      -> [a]              -- foldr turns a list
      -> b                -- into a single result
这可能需要更多的解包,而不是内联注释

我们都知道列表有两种情况,一种是包含单个元素和列表其余部分的cons单元格
,另一种是不包含任何信息的空列表

嗯,
foldr
的前两个参数正是如何处理这两种情况的说明。
a->b->b
函数参数是cons
案例的处理程序。当
foldr
遇到像
x:xs
这样的cons单元格时,它将以
x
作为第一个参数调用处理程序,该参数适合类型
a
。它传递给处理程序的第二个参数不是列表的其余部分
xs
,而是
foldr
处理
xs
的结果-记住
foldr
最终将列表转换为
b
,这符合
a->b->b
类型的第二个参数

空列表案例的“处理程序”要简单得多;由于空列表不包含更多信息,我们不需要一个函数将空列表中的信息转换为
b
,我们可以立即使用
b
值作为结果

由于这种镜像结构,许多递归函数可以通过使用适当的参数调用
foldr
来实现。考试问题当然要求你利用这一点,在
foldr
方面实现
map
,而不使用递归;
foldr
中的递归就足够了

所以你的答案应该是这样的:

map f xs = foldr _ _ xs
您不需要使用多个案例来处理空列表或cons单元格;我们通过给
foldr
的两个参数来处理这些情况。空列表的情况很明显,因为
map f[]=[]
,所以让我们继续填写
foldr
的“空列表处理程序”:

案例由第一个参数处理。记住我们上面说的话;这应该是一个函数,它接受列表的一个元素和处理列表尾部的结果。请记住,我们正在配置这个
foldr
来完成
map
的工作,“处理列表尾部的结果”将相当于已经映射了列表尾部的
f
。因此,我们所需要做的就是编写一个函数,该函数可以获取原始列表的一个元素,对其应用
f
,并将其粘贴到已映射列表的前面。因此:

map f xs = foldr applyCons [] xs
  where applyCons elem mappedRest = f elem : mappedRest

-- or

map f xs = foldr (\x xs' -> f x : xs') [] xs
我希望这两种考试都能得到满分


不过,为了进一步了解一下,让我们看看您引用的“简短而怪异”的版本
map f=foldr((:).f)[
实际上与上面的版本基本相同,只是应用了一些相当机械的简化。从这里开始:

map f xs = foldr (\x xs' -> f x : xs') [] xs
我们可以看到,该定义的两面都是xs;也就是说,
xs
=
两侧的最后一个参数,不建议在其他任何地方使用。因此,我们可以从双方都放弃它(这个过程在不必要的花哨术语中称为“eta降低”),留给我们的是:

map f = foldr (\x xs' -> f x : xs') []
然后我们可以在lambda中以前缀形式使用
,而不是作为运算符:

map f = foldr (\x xs' -> (:) (f x) xs') []
这表明,在lambda函数中,
xs'
也是参数列表中的最后一项,也是右侧的最后一个参数,因此我们也可以删除它:

map f = foldr (\x -> (:) (f x)) []
然后,您可能记得也可能不记得(但如果您记得它是如何工作的,您应该能够弄清楚),composition
操作符的定义是:

f . g = \x -> f (g x)
或者在我们的情况下:

(:) . f = \x -> (:) (f x)
右侧正是我们使用的lambda,因此我们可以将其替换为左侧:

map f = foldr ((:) . f) []
这是我们的怪异和简短的版本

我之所以说它与您可能发现的更可读的较长版本“基本相同”,是因为I c