关于仿函数的一点混乱?

时间:2011-11-08 22:34:30

标签: haskell

我试图了解仿函数是什么,我找到了这个教程/示例:

http://en.wikibooks.org/wiki/Haskell/Solutions/Applicative_Functors

 data Tree a = Node a [Tree a]

上述类型的仿函数是:

 instance Functor Tree where
   fmap f (Node a ts) = Node (f a) (map (fmap f) ts)

有人可以帮助解释他们究竟做了什么以及他们为什么这样做了?我的理解是,仿函数允许您迭代数据类型。我似乎无法理解使用的语法?

3 个答案:

答案 0 :(得分:3)

我以前从未见过Haskell,但我猜它是在定义一个数据类型(称为Tree),它由一个包含值的Node和一个Tree数组(它们将成为分支)组成原始树)。然后定义一个对函数和树进行操作的函数,并通过将函数应用于Node的值来创建一个新树,并将自身递归地应用于数组中的所有分支(使用map函数作为快捷方式)。 / p>

答案 1 :(得分:3)

基本上,在Haskell中,您可以将仿函数视为:

  1. 包含特殊上下文中的值的框(IO,Maybe,Aither a)
  2. 包含多个值(树,地图a,列表)
  3. 的结构

    此外,仿函数还有一个操作 - fmap - 了解其特定结构。

    使用fmap,您可以轻松地将结构保留转换应用于仿函数。例如,fmap (+ 1)是一个向任何仿函数添加1的函数:

    Prelude> fmap (+ 1) [1,2 ] -- using a list functor
    [2,3]
    Prelude> fmap (+ 1) (Just 2) -- using a maybe functor
    Just 3
    

    在您给出的示例中,Tree被赋予一个Functor实例 - fmap的实现 - 它可以理解树的结构,并为您提取摘要。

    Functors,Applicative Functors,Monads,Monoids的绝佳资源是Learn You A Haskell

答案 2 :(得分:3)

Functor对于两个数据表示之间的映射非常有用。有时候这可能类似迭代,有时候不是。拥有这个通用的Functor类型类允许我们忽略数据类型的实际结构(MaybeListTree),并只关注它们包含的数据。该数据类型的作者应该知道如何遍历/迭代该数据结构,以便他应该为此提供实现(以该数据类型的Functor实例的形式)。我们必须提供的是f函数,它使用a并将其映射到b。例如:

import Data.Char (toLower)

data Tree a = Node a [Tree a]
  deriving Show

instance Functor Tree where
  fmap f (Node a ts) = Node (f a) (map (fmap f) ts)

main :: IO ()
main = do print (toLower `fmap` (Node 'F' [])) -- Node 'f' []
          print (toLower `fmap` (Just 'F'))    -- Just 'f'
          print (toLower `fmap` "FOO")         -- "foo"

我们能够使用与toLower相同的代码fmap来小写这些字符。

因此,在定义Functor实例时应该做的是使用模式匹配提取内部数据,并将接收到的回调函数f应用于每个结果。

可以在fmap模块中找到Control.Applicative的中缀同义词,称为<$>

main :: IO ()
main = do print (toLower <$> (Node 'F' [])) -- Node 'f' []
          print (toLower <$> (Just 'F'))    -- Just 'f'
          print (toLower <$> "FOO")         -- "foo"