238

在阅读有关函数式编程的各种文章时,我曾多次遇到过“函子”一词,但作者通常假设读者已经理解了这个词。在网络上环顾四周,要么提供了过于技术性的描述(请参阅Wikipedia 文章),要么提供了非常模糊的描述(请参阅此ocaml-tutorial 网站上的 Functors 部分)。

有人可以定义这个术语,解释它的用途,或者提供一个如何创建和使用 Functor 的例子吗?

编辑:虽然我对这个术语背后的理论感兴趣,但我对理论的兴趣不如对概念的实现和实际使用的兴趣。

编辑2:看起来有一些交叉术语:我特别指的是函数式编程的函子,而不是C++的函数对象。

4

18 回答 18

286

“函子”一词来自范畴论,范畴论是一个非常普遍、非常抽象的数学分支。函数式语言的设计者至少以两种不同的方式借用了它。

  • 在 ML 语言家族中,函子是一个将一个或多个其他模块作为参数的模块。它被认为是一项高级功能,大多数初学者都很难使用它。

    作为实现和实际使用的示例,您可以一劳永逸地将您最喜欢的平衡二叉搜索树形式定义为函子,并且它将作为参数的模块提供:

    • 二叉树中要使用的键的类型

    • 键上的全排序函数

    完成此操作后,您可以永远使用相同的平衡二叉树实现。(存储在树中的值的类型通常是多态的——树不需要查看值,只需要复制它们,而树肯定需要能够比较键,它从函子的参数。)

    ML 函子的另一个应用是分层网络协议。链接是 CMU Fox 小组的一篇非常棒的论文;它展示了如何使用函子在更简单的层类型(如 IP 甚至直接通过以太网)上构建更复杂的协议层(如 TCP)。每一层都被实现为一个函子,它接受它下面的层作为参数。软件的结构实际上反映了人们思考问题的方式,而不是只存在于程序员头脑中的层。1994 年这部作品出版时,它是一件大事。

    对于 ML 函子的实际运行示例,您可以查看论文ML Module Mania,其中包含一个可发布(即可怕)的函子在工作中的示例。有关 ML 模块系统的精彩、清晰、清晰的解释(与其他类型的模块进行比较),请阅读 Xavier Leroy 1994 年出色的 POPL 论文Manifest Types, Modules, and Separate Compilation的前几页。

  • 在 Haskell 和一些相关的纯函数式语言中,Functor是一个类型类。当类型提供具有特定预期行为的特定操作时,类型属于类型类(或者更专业地说,类型“是”类型类的实例)。如果一个类型具有某些类似集合的行为,则它T可以属于类:Functor

    • 该类型T通过另一种类型参数化,您应该将其视为集合的元素类型。如果您分别包含整数、字符串或布尔值,则完整集合的类型类似于T Int, T String, 。T Bool如果元素类型未知,则将其写为类型参数 a,如T a.

      示例包括列表(零个或多个 type 元素a)、Maybe类型(零个或一个 type 元素a)、元素集 type a、元素数组 type a、各种包含 type 值的搜索树a,以及许多其他你能想到。

    • 另一个T必须满足的属性是,如果你有一个类型a -> b的函数(元素上的函数),那么你必须能够采用该函数并在集合上产生一个相关的函数。您可以使用 operator 来执行此操作fmap,它由类型类中的每个类型共享Functor。运算符实际上是重载的,所以如果你有一个eventype的函数Int -> Bool,那么

      fmap even
      

      是一个重载函数,可以做很多奇妙的事情:

      • 将整数列表转换为布尔值列表

      • 将整数树转换为布尔树

      • 转换NothingNothing_ Just 7_Just False

      在 Haskell 中,这个属性通过给出 的类型来表示fmap

      fmap :: (Functor t) => (a -> b) -> t a -> t b
      

      我们现在有一个 small t,这意味着“类中的任何类型Functor”。

    长话短说,在 Haskell 中,仿函数是一种集合,如果给你一个元素上的函数,它fmap会给你一个集合上的函数。正如你可以想象的那样,这是一个可以被广泛重用的想法,这就是为什么它被祝福成为 Haskell 标准库的一部分。

像往常一样,人们继续发明新的、有用的抽象,你可能想研究应用函子,最好的参考可能是Conor McBride 和 Ross Paterson的一篇名为Applicative Programming with Effects的论文。

于 2010-01-08T23:05:57.133 回答
70

这里的其他答案是完整的,但我会尝试另一种解释 FP 使用functor的方法。以此类比:

函子是一个类型为 a 的容器当它受到从a →<em>b 映射的函数时,会产生一个类型为b的容器。

与 C++ 中的抽象函数指针不同,这里的函子不是函数;相反,它是在受函数影响时表现一致的东西。

于 2010-01-08T23:08:25.707 回答
39

三个不同的意思,关系不大!

  • 在 Ocaml 中,它是一个参数化模块。见手册。我认为了解它们的最好方法是举例:(写得很快,可能有问题)

    module type Order = sig
        type t
        val compare: t -> t -> bool
    end;;
    
    
    module Integers = struct
        type t = int
        let compare x y = x > y
    end;;
    
    module ReverseOrder = functor (X: Order) -> struct
        type t = X.t
        let compare x y = X.compare y x
    end;;
    
    (* We can order reversely *)
    module K = ReverseOrder (Integers);;
    Integers.compare 3 4;;   (* this is false *)
    K.compare 3 4;;          (* this is true *)
    
    module LexicographicOrder = functor (X: Order) -> 
      functor (Y: Order) -> struct
        type t = X.t * Y.t
        let compare (a,b) (c,d) = if X.compare a c then true
                             else if X.compare c a then false
                             else Y.compare b d
    end;;
    
    (* compare lexicographically *)
    module X = LexicographicOrder (Integers) (Integers);;
    X.compare (2,3) (4,5);;
    
    module LinearSearch = functor (X: Order) -> struct
        type t = X.t array
        let find x k = 0 (* some boring code *)
    end;;
    
    module BinarySearch = functor (X: Order) -> struct
        type t = X.t array
        let find x k = 0 (* some boring code *)
    end;;
    
    (* linear search over arrays of integers *)
    module LS = LinearSearch (Integers);;
    LS.find [|1;2;3] 2;;
    (* binary search over arrays of pairs of integers, 
       sorted lexicographically *)
    module BS = BinarySearch (LexicographicOrder (Integers) (Integers));;
    BS.find [|(2,3);(4,5)|] (2,3);;
    

您现在可以快速添加许多可能的订单、形成新订单的方法、轻松地对它们进行二进制或线性搜索。通用编程 FTW。

  • 在像 Haskell 这样的函数式编程语言中,它意味着一些可以“映射”的类型构造函数(参数化类型,如列表、集合)。准确地说,函子f配备了(a -> b) -> (f a -> f b). 这起源于范畴论。您链接到的维基百科文章就是这种用法。

    class Functor f where
        fmap :: (a -> b) -> (f a -> f b)
    
    instance Functor [] where      -- lists are a functor
        fmap = map
    
    instance Functor Maybe where   -- Maybe is option in Haskell
        fmap f (Just x) = Just (f x)
        fmap f Nothing = Nothing
    
    fmap (+1) [2,3,4]   -- this is [3,4,5]
    fmap (+1) (Just 5)  -- this is Just 6
    fmap (+1) Nothing   -- this is Nothing
    

所以,这是一种特殊的类型构造函数,与 Ocaml 中的函子关系不大!

  • 在命令式语言中,它是指向函数的指针。
于 2010-01-08T22:22:35.940 回答
16

在 OCaml 中,它是一个参数化模块。

如果您了解 C++,请将 OCaml 函子视为模板。C++ 只有类模​​板,而仿函数在模块规模上工作。

函子的一个例子是 Map.Make;module StringMap = Map.Make (String);;构建一个使用字符串键映射的地图模块。

仅使用多态性是无法实现像 StringMap 这样的东西的;你需要对键做一些假设。String 模块包含对完全有序字符串类型的操作(比较等),并且仿函数将链接到 String 模块包含的操作。你可以用面向对象编程做类似的事情,但是你会有方法间接开销。

于 2010-01-08T21:30:16.017 回答
16

你得到了很多很好的答案。我会投:

在数学意义上,函子是代数上的一种特殊函数。它是将一个代数映射到另一个代数的最小函数。“极小”由函子定律表达。

有两种方法来看待这个问题。例如,列表是某种类型的函子。也就是说,给定“a”类型的代数,您可以生成包含“a”类型事物的列表的兼容代数。(例如:将元素带到包含它的单例列表的映射:f(a) = [a]) 同样,兼容性的概念由函子定律表达。

另一方面,给定一个函子 f “超过”类型 a,(即 fa 是将函子 f 应用于类型 a 的代数的结果)和来自 g 的函数:a -> b,我们可以计算一个新的函子 F = (fmap g) 将 fa 映射到 f b。简而言之,fmap 是 F 中将“函子部分”映射到“函子部分”的部分,而 g 是函数中将“代数部分”映射到“代数部分”的部分。它需要一个函数,一个仿函数,一旦完成,它也是一个仿函数。

看起来不同的语言使用不同的函子概念,但事实并非如此。他们只是在不同的代数上使用函子。OCamls 有一个模块代数,并且基于该代数的函子可以让您以“兼容”的方式将新声明附加到模块。

Haskell 仿函数不是类型类。它是具有满足类型类的自由变量的数据类型。如果您愿意深入研究数据类型(没有自由变量),您可以将数据类型重新解释为基础代数上的函子。例如:

数据 F = F Int

与 Ints 类同构。所以 F 作为值构造函数,是一个将 Int 映射到 F Int 的函数,一个等价的代数。它是一个函子。另一方面,你不能在这里免费获得 fmap。这就是模式匹配的用途。

函子有利于以代数兼容的方式将事物“附加”到代数元素上。

于 2010-07-14T20:56:45.843 回答
9

该问题的最佳答案可在 Brent Yorgey 的“Typeclassopedia”中找到。

本期 Monad Reader 包含对函子是什么的精确定义以及其他概念的许多定义以及图表。(Monoid、Applicative、Monad 和其他概念在函子中进行了解释和查看)。

http://haskell.org/sitewiki/images/8/85/TMR-Issue13.pdf

从 Typeclassopedia for Functor 摘录:“一个简单的直觉是 Functor 代表某种“容器”,以及将函数统一应用于容器中的每个元素的能力”

但实际上整个 typeclassopedia 是一本强烈推荐的读物,而且非常容易。在某种程度上,您可以将那里呈现的类型类视为与对象中的设计模式平行,因为它们为您提供给定行为或能力的词汇表。

干杯

于 2010-01-09T23:36:09.147 回答
7

Inria 网站上的 O'Reilly OCaml 书中有一个很好的示例(不幸的是,在撰写本文时,该书已被关闭)。我在加州理工学院使用的这本书中找到了一个非常相似的例子:OCaml 简介(pdf 链接)。相关部分是关于函子的章节(书中第 139 页,PDF 中的第 149 页)。

在书中,他们有一个名为 MakeSet 的仿函数,它创建一个由列表组成的数据结构,以及添加元素、确定元素是否在列表中以及查找元素的函数。用于确定它是否在集合中/不在集合中的比较函数已被参数化(这使得 MakeSet 成为函子而不是模块)。

他们还有一个实现比较功能的模块,以便它进行不区分大小写的字符串比较。

使用函子和实现比较的模块,他们可以在一行中创建一个新模块:

module SSet = MakeSet(StringCaseEqual);;

它为使用不区分大小写的比较的集合数据结构创建一个模块。如果您想创建一个使用区分大小写比较的集合,那么您只需要实现一个新的比较模块而不是一个新的数据结构模块。

Tobu 将仿函数与 C++ 中的模板进行了比较,我认为这很贴切。

于 2010-01-08T22:06:49.897 回答
6

鉴于其他答案以及我现在要发布的内容,我想说这是一个相当重载的词,但无论如何......

有关 Haskell 中“函子”一词含义的提示,请咨询 GHCi:

Prelude> :info Functor
class Functor f where
  fmap :: forall a b. (a -> b) -> f a -> f b
  (GHC.Base.<$) :: forall a b. a -> f b -> f a
        -- Defined in GHC.Base
instance Functor Maybe -- Defined in Data.Maybe
instance Functor [] -- Defined in GHC.Base
instance Functor IO -- Defined in GHC.Base

因此,基本上,Haskell 中的函子是可以映射的。另一种说法是函子是可以被视为容器的东西,可以要求它使用给定的函数来转换它包含的值;因此,对于列表,fmapmap、 for Maybefmap f (Just x) = Just (f x)等一致fmap f Nothing = Nothing

Functor typeclass小节和Learn You a Haskell for Great Good的Functors、Applicative Functors 和 Monoids部分给出了一些例子来说明这个特定概念的用处。(总结:很多地方!:-))

请注意,任何 monad 都可以被视为函子,事实上,正如 Craig Stuntz 指出的那样,最常用的函子往往是 monad... OTOH,有时将类型设为 Functor 类型类的实例很方便无需费心使它成为 Monad。(例如,在上述页面之一中提到的ZipListfrom的情况下。)Control.Applicative

于 2010-01-08T22:18:32.750 回答
6

“函子是对象和态射的映射,它保留了类别的组成和身份。”

让我们定义什么是类别?

这是一堆物体!

在一个圆圈内画几个点(现在是 2 个点,一个是 'a' 另一个是 'b'),现在将该圆圈命名为 A(类别)。

类别包含什么?

对象之间的组合和每个对象的标识函数。

因此,我们必须在应用 Functor 之后映射对象并保留组合。

让我们想象'A'是我们的类别,它有对象 ['a', 'b'] 并且存在一个态射 a -> b

现在,我们必须定义一个函子,它可以将这些对象和态射映射到另一个类别“B”。

假设函子被称为“也许”

data Maybe a = Nothing | Just a

所以,“B”类看起来像这样。

请再画一个圆圈,但这次用“Maybe a”和“Maybe b”代替“a”和“b”。

一切似乎都很好,所有对象都已映射

'a' 变成了 'Maybe a' 而 'b' 变成了 'Maybe b'。

但问题是我们还必须将态射从“a”映射到“b”。

这意味着 'A' 中的态射 a -> b 应该映射到态射 'Maybe a' -> 'Maybe b'

来自 a -> b 的态射称为 f,然后来自 'Maybe a' -> 'Maybe b' 的态射称为 'fmap f'

现在让我们看看'f'在'A'中做了什么,看看我们是否可以在'B'中复制它

'A'中'f'的函数定义:

f :: a -> b

f 接受 a 并返回 b

'B' 中 'f' 的函数定义:

f :: Maybe a -> Maybe b

f 接受 Maybe a 并返回 Maybe b

让我们看看如何使用 fmap 将函数 'f' 从 'A' 映射到函数 'fmap f' 在 'B'

fmap的定义

fmap :: (a -> b) -> (Maybe a -> Maybe b)
fmap f Nothing = Nothing
fmap f (Just x) = Just(f x)

那么,我们在这里做什么?

我们将函数“f”应用于“a”类型的“x”。'Nothing' 的特殊模式匹配来自Functor Maybe.

因此,我们将对象 [a, b] 和态射 [f] 从类别“A”映射到类别“B”。

那是函子!

在此处输入图像描述

于 2017-03-16T13:11:44.947 回答
5

这是一篇关于编程 POV 中的函子的文章,随后更具体地介绍了它们如何在编程语言中出现

函子的实际用途是在 monad 中,如果你寻找它,你可以找到许多关于 monad 的教程。

于 2010-01-08T21:31:09.977 回答
5

在对最高投票答案的评论中,用户Wei Hu问道:

我了解 ML 函子和 Haskell 函子,但缺乏将它们联系在一起的洞察力。在范畴理论的意义上,这两者之间有什么关系?

注意:我不懂ML,所以请原谅并纠正任何相关的错误。

让我们首先假设我们都熟悉“类别”和“函子”的定义。

一个简洁的答案是“Haskell-functors”是(endo-)functors F : Hask -> Hask,而“ML-functors”是 functors G : ML -> ML'

这里,Hask是由 Haskell 类型和它们之间的函数形成的类别,同样ML,和ML'是由 ML 结构定义的类别。

注意:制作类别存在一些技术问题Hask,但有一些方法可以解决这些问题。

从范畴论的角度来看,这意味着Hask-functor 是FHaskell 类型的映射:

data F a = ...

连同一张fmapHaskell 函数图:

instance Functor F where
    fmap f = ...

ML 几乎是一样的,虽然fmap我知道没有规范的抽象,所以让我们定义一个:

signature FUNCTOR = sig
  type 'a f
  val fmap: 'a -> 'b -> 'a f -> 'b f
end

那就是fmaps ML-types 和fmapmaps -functions ML,所以

functor StructB (StructA : SigA) :> FUNCTOR =
struct
  fmap g = ...
  ...
end

是一个函子F: StructA -> StructB

于 2014-04-23T06:43:28.860 回答
5

粗略概述

在函数式编程中,函子本质上是一种将普通一元函数(即具有一个参数的函数)提升到新类型变量之间的函数的构造。在普通对象之间编写和维护简单的函数并使用函子来提升它们,然后在复杂的容器对象之间手动编写函数要容易得多。另一个优点是只编写一次普通函数,然后通过不同的函子重新使用它们。

仿函数的示例包括数组、“可能”和“任一”仿函数、期货(参见例如https://github.com/Avaq/Fluture)等等。

插图

考虑从名字和姓氏构造完整人名的函数。我们可以将它定义fullName(firstName, lastName)为两个参数的函数,但是这不适用于只处理一个参数的函数的函子。为了补救,我们将所有参数收集在一个对象name中,现在它成为函数的单个参数:

// In JavaScript notation
fullName = name => name.firstName + ' ' + name.lastName

现在,如果数组中有很多人怎么办?无需手动查看列表,我们可以通过为具有短单行代码的数组提供fullName的方法简单地重用我们的函数:map

fullNameList = nameList => nameList.map(fullName)

并像使用它一样

nameList = [
    {firstName: 'Steve', lastName: 'Jobs'},
    {firstName: 'Bill', lastName: 'Gates'}
]

fullNames = fullNameList(nameList) 
// => ['Steve Jobs', 'Bill Gates']

这将起作用,只要我们nameList中的每个条目都是提供firstNamelastName属性的对象。但是如果有些对象没有(甚至根本不是对象)怎么办?为了避免错误并使代码更安全,我们可以将对象包装到Maybe类型中(例如https://sanctuary.js.org/#maybe-type):

// function to test name for validity
isValidName = name => 
    (typeof name === 'object') 
    && (typeof name.firstName === 'string')
    && (typeof name.lastName === 'string')

// wrap into the Maybe type
maybeName = name => 
    isValidName(name) ? Just(name) : Nothing()

whereJust(name)是一个只携带有效名称的容器,Nothing()是用于其他所有内容的特殊值。现在,无需中断(或忘记)检查参数的有效性,我们可以简单地使用fullName另一行代码重用(提升)我们的原始函数,再次基于map方法,这次为 Maybe 类型提供:

// Maybe Object -> Maybe String
maybeFullName = maybeName => maybeName.map(fullName)

并像使用它一样

justSteve = maybeName(
    {firstName: 'Steve', lastName: 'Jobs'}
) // => Just({firstName: 'Steve', lastName: 'Jobs'})

notSteve = maybeName(
    {lastName: 'SomeJobs'}
) // => Nothing()

steveFN = maybeFullName(justSteve)
// => Just('Steve Jobs')

notSteveFN = maybeFullName(notSteve)
// => Nothing()

范畴论

范畴论中的函子是两个范畴之间关于其态射组成的映射。在计算机语言中,感兴趣的主要类别是其对象类型(某些值集)并且其态射是从一种类型到另一种类型的函数的类别。f:a->bab

例如,取aString类型,b数字类型,f为将字符串映射到其长度的函数:

// f :: String -> Number
f = str => str.length

这里a = String表示所有字符串b = Number的集合和所有数字的集合。从这个意义上说,两者都a表示bSet Category中的对象(与类型的类别密切相关,这里的区别无关紧要)。在集合范畴中,两个集合之间的态射正是从第一个集合到第二个集合的所有函数。所以我们这里的长度函数f是从字符串集合到数字集合的态射。

由于我们只考虑集合范畴,从它到自身的相关函是映射发送对象到对象和态射到态射,满足某些代数定律。

例子:Array

Array可能意味着很多东西,但只有一件事是 Functor - 类型构造,将类型映射a到 type[a]的所有数组的类型a。例如,Array函子将类型映射String 到类型[String](所有任意长度的字符串数组的集合),并将类型映射Number到相应的类型[Number](所有数字数组的集合)。

重要的是不要混淆 Functor 映射

Array :: a => [a]

带态射a -> [a]。函子只是将类型映射(关联)a到类型[a]中,作为一个事物到另一个事物。每种类型实际上都是一组元素,在这里无关紧要。相反,态射是这些集合之间的实际函数。例如,有一个自然态射(函数)

pure :: a -> [a]
pure = x => [x]

它将一个值发送到 1 元素数组中,该值作为单个条目。该函数不是ArrayFunctor的一部分!从这个函子的角度来看,pure它只是一个和其他函数一样的函数,没什么特别的。

另一方面,Array函子有它的第二部分——态射部分。它将态射映射f :: a -> b到态射[f] :: [a] -> [b]

// a -> [a]
Array.map(f) = arr => arr.map(f)

这里arr是任意长度且值为 type 的任意数组a,并且arr.map(f)是具有相同长度且值为 type 的数组b,其条目是应用于f的条目的结果arr。为了使它成为一个函子,映射恒等式到恒等式和组合映射到组合的数学定律必须成立,这在这个Array例子中很容易检查。

于 2016-12-28T19:57:10.520 回答
2

不与先前的理论或数学答案相矛盾,但 Functor 也是一个对象(在面向对象的编程语言中),它只有一种方法并有效地用作函数。

一个例子是 Java 中的 Runnable 接口,它只有一个方法:run。

考虑这个例子,首先在 Javascript 中,它具有一流的功能:

[1, 2, 5, 10].map(function(x) { return x*x; });

输出:[1、4、25、100]

map 方法接受一个函数并返回一个新数组,其中每个元素都是将该函数应用于原始数组中相同位置的值的结果。

做同样的事情是Java,使用Functor,你首先需要定义一个接口,比如:

public interface IntMapFunction {
  public int f(int x);
}

然后,如果您添加一个具有地图功能的集合类,您可以这样做:

myCollection.map(new IntMapFunction() { public int f(int x) { return x * x; } });

这使用 IntMapFunction 的内联子类来创建 Functor,它是早期 JavaScript 示例中的函数的 OO 等价物。

使用 Functor 可以让您在 OO 语言中应用函数式技术。当然,一些面向对象语言也直接支持函数,所以这不是必需的。

参考:http ://en.wikipedia.org/wiki/Function_object

于 2014-11-29T01:28:16.040 回答
2

KISS:函子是一个具有 map 方法的对象。

JavaScript 中的数组实现了映射,因此是函子。Promise、Streams 和 Trees 经常用函数式语言实现 map,当它们这样做时,它们被认为是函子。函子的 map 方法获取它自己的内容,并使用传递给 map 的转换回调对每个内容进行转换,并返回一个新函子,该函子包含作为第一个函子的结构,但具有转换后的值。

来源:https : //www.youtube.com/watch?v= DisD9ftUyCk&feature=youtu.be&t=76

于 2016-03-07T22:05:54.210 回答
0

在函数式编程中,错误处理是不同的。抛出和捕获异常是命令式代码。不是使用try/catch块,而是在可能引发错误的代码周围创建一个安全框。这是函数式编程中的基本设计模式。包装器对象用于封装潜在的错误值。包装器的主要目的是提供一种“不同”的方式来使用被包装的对象

 const wrap = (val) => new Wrapper(val);

包装守卫直接访问这些值,因此可以安全且不可变地操作它们。因为我们无法直接访问它,所以提取它的唯一方法是使用identity function.

identity :: (a) -> a

这是标识函数的另一个用例:从封装类型中提取数据。

在此处输入图像描述

Wrapper 类型使用映射来安全地访问和操作值。在这种情况下,我们将标识函数映射到容器上以从容器中提取值。使用这种方法,我们可以在调用函数之前检查 null,或者检查空字符串、负数等。

 fmap :: (A -> B) -> Wrapper[A] -> Wrapper[B]

fmap,首先打开容器,然后将给定函数应用于其值,最后将值关闭回相同类型的新容器中。这种类型的函数称为 a functor

  • fmap 在每次调用时返回容器的新副本。

  • 函子没有副作用

  • 函子必须是可组合的

于 2022-02-21T18:19:44.590 回答
-4

实际上,函子是指在 C++ 中实现调用运算符的对象。在 ocaml 中,我认为函子是指将模块作为输入并输出另一个模块的东西。

于 2014-09-09T22:22:27.750 回答
-6

简而言之,仿函数或函数对象是可以像函数一样调用的类对象。

在 C++ 中:

这就是你编写函数的方式

void foo()
{
    cout << "Hello, world! I'm a function!";
}

这就是你编写仿函数的方式

class FunctorClass
{
    public:
    void operator ()
    {
        cout << "Hello, world! I'm a functor!";
    }
};

现在你可以这样做:

foo(); //result: Hello, World! I'm a function!

FunctorClass bar;
bar(); //result: Hello, World! I'm a functor!

使这些如此出色的是您可以在类中保持状态 - 想象一下,如果您想询问一个函数它被调用了多少次。没有办法以简洁、封装的方式做到这一点。使用函数对象,它就像任何其他类一样:您将拥有一些实例变量,您可以在其中增加一些实例变量operator ()和一些方法来检查该变量,并且一切都可以随心所欲。

于 2010-01-08T21:41:58.223 回答
-11

Functor 与函数式编程没有特别的关系。它只是一个指向函数或某种对象的“指针”,可以像函数一样调用它。

于 2010-01-08T21:31:24.343 回答