Python的地图和Haskell的fmap有什么区别?

时间:2017-07-17 06:40:11

标签: python haskell

我知道他们都是不同的语言,但他们两者基本上都不一样吗? fmap在Functor上应用函数,而Python的map在iterable上应用函数。因此,如果两者之间存在任何差异,您能提供一些我们可以用Python的地图而不是Haskell的fmap(反之亦然)的例子吗?

3 个答案:

答案 0 :(得分:12)

他们非常不同。

这两个函数在列表类似的东西上做了非常类似的事情,但这确实是相似之处的结束。将Haskell的map与Python map进行比较可能更有用,但fmap更通用,意义也不同。

在Haskell中,fmap是一种“提升”或“刺穿”操作。它需要a -> b的函数并将其“提升”到f a -> f b的函数。类似列表的容器是一种可以被“刺穿”的东西,但是除了列表之外还有很多东西是函子!

Maybe a上的映射非常有用且常见,但有人可能认为Maybe就像一个零或一个元素的列表。但是,请考虑 functions 是Haskell中的函子的事实。你可以在一个函数上fmap并接收一个新函数,但函数显然不是迭代的。类似地,解析器组合库(如parsec)中的解析器是仿函数,您可以通过fmap ping它们来生成新的解析器,以将函数应用于它们的结果。

各种各样的东西都是仿函数,其中很多都与容纳价值的容器关系不大。所有monad都是仿函数,包括WriterStateCont之类的东西,它们都是有效的计算,而不是类似列表的东西。其他东西也是仿函数,例如Const,它可以与Lift组合以形成一个单独收集错误的应用函子。

这些事物的具体内容和具体内容超出了这个问题的范围,但重点是Haskell的一个仿函数概念比一个“可迭代的东西”更为普遍,而且它通常用于与Python的map不同的方式。

然而,从另一个角度来看,可以说Python的map比Haskell的fmap更强大。普遍性需要成本,这需要简单。具体而言,您可以选择做更多的事情!这与Python与Haskell的其他差异(如动态类型和可变函数)相结合,使得Python的map可以执行Haskell fmap无法做到的事情。

正如Daniel Sanchez指出的那样,Python的map是可变的,所以当提供多个迭代时,它在Haskell中就像zipWith一样:

>>> map(lambda x,y: x+y, range(10), range(10))
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
但是,还有一点。 Python的map也可以在不同类型的东西上运行。在Haskell中,Functor类的通用性很有用,主要是因为它可以编写对任何Functor进行操作的函数,这意味着所有Functor需要在更多或{不一样的方式。 f a内部的元素,如果您想使用容器类比,必须属于a类型。

考虑这对于哈希映射或字典这样的含义。在Haskell中,Functor必须具有* -> *种类,因此如果部分应用,则Map只能是Functor。因此,Map kFunctor个实例,因此fmap仅对Map进行操作。

在Python中,类型系统的后果并没有如此严格地强制执行,因此在迭代时,词典实际上充当了的序列:

>>> map(None, {'foo': 1, 'bar': 2})
['foo', 'bar']

哦,这个例子还演示了另一个Python奇怪映射None,而不是映射身份函数的行为,但将其映射到多个事物就像Haskell的zip一样,并产生元组:

>>> map(None, [1, 2, 3], [4, 5, 6])
[(1, 4), (2, 5), (3, 6)]

最后,请注意Haskell的fmap无法映射集合!为什么是这样?嗯,刚性类型系统要求fmap适用于任何值,但Set必须由具有Ord实例的类型的值组成。 Python不需要跳过任何这样的箍,所以它可以映射就好了:

>>> map(None, {1, 2, 3})
[1, 2, 3]

Python和Haskell是非常不同的语言。很多这些差异只是语言差异的工件,而不一定是功能设计的差异。

仍然,核心差异很容易总结:Python的map用于映射类似列表的事物,但Haskell的fmap用于提升函数以对任意数据结构进行操作。即使你看过语言差异,这两件事情也是截然不同的,所以不要认为它们只是因为它们在表面上有相同的形状。

答案 1 :(得分:3)

Python map有一个Haskell没有的行为:

map(lambda x,y: x+y, range(10), range(10))
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

它对n次迭代的迭代进行迭代,从它们解包值并将它们作为参数传递给函数。 实际上Haskell map由于其类型签名而无法做到这一点 fmap :: Functor f => (a -> b) -> f a -> f b

python map的用法类似于Haskell zipWith

Prelude> zipWith (\x y -> x+y) [1..10] [1..10]
[2,4,6,8,10,12,14,16,18,20]

答案 2 :(得分:1)

Haskell both ; PKfmap的概括,可以与任何map一起使用,而不仅仅是列表。

Functor

查看map :: (a -> b) -> [a] -> [b] map f [] = [] map f (x:xs) = f x : map f xs ,您可以立即看到相似性;我们只需用任意仿函数fmap替换列表。

f

正如您所料,列表的实例是微不足道的;我们只使用我们已定义的class Functor f where fmap :: (a -> b) -> f a -> f b 函数。

map

instance Functor [] where fmap = map 可以类似地定义类似但不同于列表的类型。

fmap

即使对于更复杂的类型,例如树,instance Functor Maybe where -- Here, f is a variable representing the function to map, not the -- Functor type fmap f Nothing = Nothing -- Nothing instead of an empty list fmap f (Just x) = Just (f x) -- Just x is like a singleton list 仍然有效地将一个函数应用于容器的每个元素,保留容器的结构,就像它对列表和fmap一样。空树就像一个空列表或Maybe;其他树就像列表,除了不是递归映射列表的尾部,你递归地映射两个子树。

Nothing