我知道他们都是不同的语言,但他们两者基本上都不一样吗? fmap在Functor上应用函数,而Python的map在iterable上应用函数。因此,如果两者之间存在任何差异,您能提供一些我们可以用Python的地图而不是Haskell的fmap(反之亦然)的例子吗?
答案 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都是仿函数,包括Writer
,State
和Cont
之类的东西,它们都是有效的计算,而不是类似列表的东西。其他东西也是仿函数,例如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 k
有Functor
个实例,因此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 ; PK
是fmap
的概括,可以与任何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