是否可以使用地图定义foldr?

时间:2014-05-24 22:22:33

标签: haskell fold higher-order-functions map-function

在我使用map定义foldr之后,我想到了一个问题:

如果可以使用map定义foldr,那么相反呢?

从我的观点来看,这是不可能的,但我找不到合适的解释。 谢谢你的帮助!

3 个答案:

答案 0 :(得分:15)

让我们从一些类型的签名开始。

foldr :: (a -> b -> b) -> b -> [a] -> b
map :: (a -> b) -> [a] -> [b]

我们可以使用map来模拟fold,因为fold是一个通用运算符(here是一个关于此属性的更数学但相当友好的论文)。

我确信使用map来模拟foldr有一些创造性的方法。这肯定是一个有趣的练习。但我不认为这是一个直截了当的,而不是疯狂的无瑕疵"解决方案,并且为了解释它,让我们暂时忘记foldr并专注于一个更简单的累积功能:

sum :: [Int] -> Int

sum == foldr (+) 0,表示foldr实现sum。如果我们可以foldr实施map,我们绝对可以sum实施map。我们能做到吗?

我认为sum的签名是一个惊天动地 - sum返回Intmap总是返回一些内容。所以也许map可以完成繁重的工作,但我们仍然需要另一种类型为[a] -> a的函数才能获得最终结果。在我们的例子中,我们需要一个[Int] -> Int类型的函数。这是非常不幸的,因为这正是我们首先想要避免的。

所以我猜答案是:您可以使用foldr实施map - 但它可能需要使用foldr:)

答案 1 :(得分:5)

查看它的最简单方法是看map保留列表的主干。如果你看一下更通用的fmap(这是一个地图,但不仅仅是列表而是一般的Functor),它甚至是一个法则

fmap id = id

有许多方法可以“作弊”,但在对问题的最直接解释中,折叠比地图更为通用。有一个很好的技巧,在Edward Kmett的镜头库中使用了很多。考虑Const monad,其定义如下:

newtype Const a b = Const { runConst :: a }

instance Functor (Const a) where fmap _ (Const a) = Const a
instance (Monoid a) => Monad (Const a) where
    return _ = Const mempty
    Const a >>= Const b = Const (a <> b)

现在你可以根据monadic map operation mapM制定一个折叠,只要结果类型是monoidal:

fold :: Monoid m => [m] -> m
fold = runConst . mapM Const

答案 2 :(得分:2)

如果你做某种作弊辅助功能:

f [x] a = x a
f (x:xs) a = f xs (x a)

foldr g i xs = f (map g $ reverse xs) i