在函数式编程中,什么是函子?


224

在阅读有关函数式编程的各种文章时,我遇到过几次“ Functor”一词,但是作者通常认为读者已经理解了该词。在网上四处浏览时,要么提供了过多的技术说明(请参阅Wikipedia文章),要么提供了令人难以置信的模糊说明(请参阅此ocaml-tutorial网站上有关Functors的部分)。

有人可以友好地定义该术语,解释其用法,还可以提供一个有关如何创建和使用Functor的示例吗?

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

编辑2:似乎正在进行一些跨终端操作:我专门指的是函数编程的Functors,而不是C ++的函数对象。




如果您对实际实现和使用感兴趣,而不是对概念背后的平流层术语和理论感兴趣,则只需要一个衬套即可:函子公开了“地图”功能。
理查德·戈麦斯

@RichardGomes恕我直言,我认为它将函子的作用简化为类似于Java的简单接口,事实并非如此。函子会转换内容,它会根据现有的类型(在Haskell中)构建新的类型,这意味着这些类型也会被映射。fmap映射功能。涉及两种映射。这种看待事物的方式将有助于理解范畴论(更普遍)。我的意思是理解基础类别理论以帮助我们处理Haskell中的所有类别理论(有趣的东西,单子,...)。
Ludovic Kuty

@VladtheImpala博客文章很棒,但是,即使它有很大帮助,我也要记住,函子可以构建(映射到)另一种类型。我特别喜欢Monads中的句子“函子F接受每个T类型并将其映射到新的FT”,就像墨西哥卷饼。恕我直言,它不仅是围绕值的上下文(框),即使事实证明可以看到这样的事情(Haskell PoV与类别理论PoV?)
Ludovic Kuty

Answers:


273

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

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

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

    • 二叉树中使用的密钥类型

    • 键的总排序功能

    完成此操作后,就可以永久使用相同的平衡二叉树实现。(存储在树中的值的类型通常是多态的-树除了复制它们之外不需要查看其他值,而树肯定需要能够比较键,并且它从函子的参数。)

    ML函数的另一个应用是分层网络协议。链接是由CMU Fox集团撰写的,非常出色的论文。它显示了如何使用仿函数在更简单的层类型(例如IP或直接通过以太网)上构建更复杂的协议层(例如TCP)。每一层都实现为函子,该函子将其下一层作为参数。该软件的结构实际上反映了人们对问题的思考方式,而不是仅仅存在于程序员脑海中的层。1994年,当这项工作出版时,这很重要。

    有关运行中的ML仿函数的一个疯狂例子,您可以看到ML Module Mania论文,其中包含一个可发布的(即令人恐惧的)仿函数实例。要获得关于ML模块系统的精妙,清晰,清晰的解释(与其他模块的比较),请阅读Xavier Leroy 1994年出色的POPL论文清单类型,模块和单独编译的前几页。

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

    • T在另一个类型上对类型进行参数化,您应该将其视为集合的元素类型。该类型全额征收的是事遂所愿T IntT StringT Bool,如果你分别包含整数,字符串或布尔值。如果元素类型未知,则将其写为类型参数 a,如中所示T a

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

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

      fmap even

      是一个重载函数,可以完成许多美妙的事情:

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

      • 将整数树转换为布尔树

      • 转换NothingNothingJust 7Just False

      在Haskell中,此属性通过指定以下类型来表示fmap

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

      现在我们有一个small t,表示“类中的任何类型Functor”。

    长话短说,在Haskell中,函子是一种集合,如果给定了元素上的函数,则函子fmap将返回集合上的函数。可以想象,这是一个可以广泛重用的想法,这就是为什么它作为Haskell标准库的一部分而受到赞誉的原因。

像往常一样,人们继续发明新的,有用的抽象,并且您可能想研究应用函子,对此最好的参考可能是Conor McBride和Ross Paterson撰写的名为“ 具有效果应用编程”的论文。


7
我了解ML功能和Haskell功能,但缺乏将它们联系在一起的见识。从类别理论的角度来看,这两者之间是什么关系?
胡虎2010年

6
@韦虎:范畴论对我从来没有任何意义。我能说的最好的是,这三个概念都涉及映射。
Norman Ramsey 2010年

16
根据此Haskell Wiki:en.wikibooks.org/wiki/Haskell/Category_theory,就像这样:一个类别是对象和态素(函数)的集合,其中态素是从类别中的对象到该类别中的其他对象的。函子是一种将对象和形态射影从一个类别映射到另一类别的对象和射影的函数。至少这就是我的理解。这对我尚不了解的编程意味着什么。
paul 2010年

5
@ norman-ramsey,您看过Lawvere和Schanuel的概念数学吗?我是该地区的新手,但本书可读性强,而且-敢说-令人愉快。(喜欢您的解释。)
Ram Rajamony 2014年

2
then you have to be able to take that function and product a related function on collections您是produce不是要说product
问题人员

64

此处的其他答案是完整的,但我将尝试对FP对functor的使用的另一种解释。以此为类推:

函子是类型的容器一个,当经受一个函数,从地图一个b,产生类型的容器b

与C ++中使用的抽象函数指针不同,此处的函子不是函数。相反,它在接受某个函数时会表现一致。


3
类型b的容器表示“与输入容器相同类型的容器,但现在已填充b的容器”。因此,如果我们有一个香蕉列表,并且我们映射一个接受香蕉并输出水果沙拉的函数,那么现在我们有了一个水果沙拉列表。同样,如果我们有一棵香蕉树,并且映射了相同的功能,那么现在将有一棵苹果树。等列表两种仿函数在这里。
qqwy

3
“函子是类型a的容器,当接受函数时”-实际上是相反的-函数(态射)受函子约束,将映射到另一个射态
Dmitri Zaitsev

38

有三种不同的含义,没有多大关系!

  • 在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)。这起源于范畴论。您链接到的Wikipedia文章就是这种用法。

    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中的函子无关!

  • 在命令式语言中,它是功能的指针。

此注释的后三行中的<q> map </ q>不应确实是<q> fmap </ q>吗?
imz-伊万·扎哈拉里谢夫(Ivan Zakharyaschev)2010年

1
我一直读到函子是容器-但这只是一个简单的简化。您的答案最终提供了缺少的链接:函数是参数化类型(类型构造函数)的类型类(类型约束)。就这么简单!

16

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

如果您了解C ++,可以将OCaml函子视为模板。C ++仅具有类模板,函子在模块级别起作用。

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

仅仅通过多态性就无法实现StringMap之类的东西。您需要对键进行一些假设。String模块包含按完全有序的字符串类型进行的操作(比较等),并且函子将链接到String模块包含的操作。您可以使用面向对象的编程做类似的事情,但是会产生方法间接开销。


我是从ocaml网站上获得的-但我不知道参数化模块的用途。
艾里克·福布斯

4
@Kornel是的,我描述的是OCaml概念。另一个概念只是“功能价值”,在FP中没有什么特别的。@Erik我略有扩展,但参考文档的加载速度很慢。
东武

13

您有很多不错的答案。我会介绍:

数学上的函子是代数上的一种特殊函数。这是将代数映射到另一个代数的最小函数。极小值由函子定律表示。

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

另一方面,给定一个函子f在类型a上(即fa是将函子f应用于类型a的代数的结果),并从g获得函数:a-> b,我们可以计算出一个新的函子F =(fmap g),它将fa映射到f b。简而言之,fmap是F的一部分,它将“ functor parts”映射到“ functor parts”,而g是该函数的一部分,将“代数部分”映射到“代数部分”。它需要一个函数,一个函子,一旦完成,它也是一个函子。

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

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

数据F = F Int

与Ints类同构。因此,F作为值构造函数,是一个将Int映射到F Int(等效代数)的函数。它是一个函子。另一方面,您不会在这里免费获得fmap。这就是模式匹配的目的。

函子对于以代数兼容方式将事物“附加”到代数元素上很有好处。


8

这个问题的最佳答案是在Brent Yorgey的“ Typeclassopedia”中找到的。

本期《 Monad Reader》包含对函子的精确定义以及其他概念和图表的许多定义。(针对函子对Monoid,Applicative,Monad和其他概念进行了解释和介绍)。

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

来自Typeclassopedia的Functor摘录:“一个简单的直觉是Functor代表某种“容器”,并且具有将功能统一应用于容器中每个元素的能力”

但实际上,强烈建议您阅读整本类别的百科全书,这非常容易。从某种意义上说,您可以看到那里的类型类与对象中的设计模式相类似,因为它们为您提供了给定行为或功能的词汇。

干杯


7

在Inria网站上的O'Reilly OCaml书中有一个很好的例子(不幸的是,撰写本书时,该记录还很低)。在此书中,我找到了由caltech使用的非常相似的示例:OCaml简介(pdf链接)。相关章节是有关函子的章节(本书中的第139页,PDF中的第149页)。

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

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

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

module SSet = MakeSet(StringCaseEqual);;

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

Tobu将函子与C ++中的模板进行了比较,我认为这很合适。


6

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

有关Haskell中“ functor”一词含义的提示,请询问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中的函子是可以映射的。换句话说,函子是可以被视为容器的容器,可以要求它使用给定的函数来转换它所包含的值。因而,对于列表,fmap重合有map,为Maybefmap f (Just x) = Just (f x)fmap f Nothing = Nothing等。

Functor类型类小节以及有关Functor ,应用FunctorMonoids的部分您了解Haskell实现卓越,给出了一些示例,说明该特定概念在哪些方面有用。(摘要:很多地方!:-)

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


5

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

函子的实际用法是在monad中,如果需要,可以找到许多有关monad的教程。


1
“函子的实际使用是在monad中”-不仅如此。所有单子函数都是函子,但非单子函数有很多用途。
amindfv 2013年

1
我想说,学习单子以使用函子就像节省劳斯莱斯去购买食品。
Marco Faustinelli 2015年

5

在对最受好评的答案的评论中,用户Wei Hu问:

我了解ML功能和Haskell功能,但缺乏将它们联系在一起的见识。从类别理论的角度来看,这两者之间是什么关系?

注意:我不懂ML,因此请原谅并更正任何相关的错误。

首先,假设我们都熟悉“类别”和“功能部件”的定义。

一个简单的答案是“ Haskell-functors”是(内-)functors,F : Hask -> Hask而“ ML-functors”是仿函数G : ML -> ML'

在这里,Hask是由Haskell类型和它们之间的函数形成的类别,并且类似地MLML'是由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

这是f地图ML-types和fmap地图ML-functions,所以

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

是一个函子F: StructA -> StructB


5

“ Functor是对象和形态学的映射,可以保留类别的组成和标识。”

让我们定义什么是类别?

一堆东​​西!

在一个圆圈内画几个点(目前为2个点,一个为'a',另一个为'b'),并暂时将其命名为A(类别)。

类别包含什么?

对象之间的组成以及每个对象的身份功能。

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

假设'A'是我们的类别,其中包含对象['a','b'],并且存在一个态射a-> b

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

假设函子被称为“也许”

data Maybe a = Nothing | Just a

因此,类别“ B”看起来像这样。

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

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

“ a”变成“也许a”,“ b”变成“也许b”。

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

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

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

现在,让我们看看“ f”在“ A”中正在执行什么功能,看看是否可以在“ B”中复制它

“ A”中“ f”的功能定义:

f :: a -> b

f取a并返回b

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

f :: Maybe a -> Maybe b

f可能是a,然后可能是b

让我们看看如何使用fmap将函数“ f”从“ A”映射到“ B”中的函数“ fmap f”

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”。

多数民众赞成在Functor!

在此处输入图片说明


有趣的答案。我想用Monad来补充它,就像墨西哥卷饼(对抽象,直觉和“ monad谬论”的滑稽回答)和他的句子“函子F接受每个T类型并将其映射到新的FT类型”,也就是类型构造函数。函数式编程和类别理论-类别和函子也很有用。
Ludovic Kuty

3

概述

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

函子的示例包括数组,“也许”和“任一个”函子,期货(请参见例如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()

其中Just(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->b从一种类型a到另一种类型的函数的对象b

例如,将a其作为String类型,b数字类型,并将f该函数映射为字符串的长度:

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

这里a = String表示所有字符串b = Number的集合和所有数字的集合。从这个意义上讲,a和都b代表Set类别中的对象(这与类型的类别密切相关,区别在此无关紧要)。在集合类别中,两个集合之间的态射恰好是从第一个集合到第二个集合的所有函数。因此,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元素数组中,该值作为单个条目。该功能不是Array Functor 的一部分!从该函子的角度来看,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示例中易于检查。


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中等效。

使用函子可以使您以OO语言应用功能技术。当然,某些OO语言也直接支持功能,因此这不是必需的。

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


实际上,“功能对象”不是对函子的正确描述。例如,Array是一个函子,但Array(value)仅给出1元素数组。
德米特里·扎伊采夫

0

吻:函子是具有map方法的对象。

JavaScript中的数组实现了映射,因此是函子。承诺,流和树通常以功能语言实现地图,并且当它们实现时,它们被视为函子。仿函数的map方法获取其自己的内容,并使用传递给map的转换回调对每个内容进行变换,然后返回一个新的仿函数,其中包含该结构作为第一个仿函数,但具有转换后的值。

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


1
附带说明一下,“对象”应该非常宽泛地理解,只是意味着“某物”。例如,对于OOP语言,用object代替class。可能会说“函子是实现Functor接口的类”(当然,该接口可能不在物理上,但是您可以将“ map”逻辑提升到该接口,并让所有可映射类共享它) -只要您的类型系统允许以这种通用方式键入内容,那就是。
qqwy

1
老实说,我发现类超级令人困惑,一方面,它们只是一些具体事物的蓝图,但它们也可能具有方法(静态内容),并且可以像对象一样工作。类是否实现接口或它创建的实例?
soundyogi

1
是的,他们可能会造成混乱。但是:类实现接口(它们“填充”接口方法中给出的空白。换句话说:它们将接口的抽象准则转换为可以立即(原谅双关)实例化的具体准则)。关于“类的行为就像对象”:在真正的OOP语言(如Ruby)中,类是“类”类的实例。一直都是乌龟。
Qqwy

Array类型构造定义单个函子。它的实例也称为“数组”,但它们不是函子。这里的描述应该更加精确。
德米特里·扎伊采夫

@DmitriZaitsev您能详细说明吗?因此,您要说的是实例不是函子?我看不到这种感觉,因为您可以通过映射来获得新的函子。
Soundyogi

-4

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


-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 ()并使用某种方法来检查该变量,一切都随您所愿。


12
不,那些函子不是FP语言所使用的类型理论概念。
东武

1
我可以看到有人可以证明FunctorClass满足第一函子定律,但是您能为第二定律草绘出证明吗?我不太明白。
约尔格W¯¯米塔格

3
ah,你们是对的。我试图解决“网络提供了极其详尽的技术说明”,但为了避免出现这样的问题,我试图避免“在ML系列语言中,函子是一个将一个或多个其他模块作为参数的模块”。但是,这个答案是不好的。过于简化和规格不足。我很乐意删除它,但我留给后代在:)摇头:
Matt

我很高兴您留下答案和评论,因为它有助于解决问题。谢谢!我遇到了麻烦,因为大多数答案都是用Haskell或OCaml编写的,对我而言,这有点像用鳄鱼解释鳄鱼。
罗布2014年

-10

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


8
有一个特定的FP函子概念(来自类别理论),但您说对了,在非FP语言中,同一单词也用于其他事物。
Craig Stuntz 2010年

您确定函数指针是函子吗?我看不到函数指针如何满足这两个函子定律,尤其是第二函子定律(形态学成分的保存)。你有证明吗?(只是一个粗略的草图。)
JörgW Mittag
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.