函数对象(function object)[note 1]是一个程序设计的对象允许被当作普通函数来调用。

与之相区分:

  • functionoid(函数体对象)定义为“有一个主要方法”的对象(object),不要求是什么类型,只要能 “调用” 就行。例如:struct LessThan { bool compare(int a, int b) const { return a < b; } };
    • functor(仿函数)定义为:重载了 operator() 的类对象(class object)。在 C++ 里,functor 就是 function object。
      • lambda对象,生成一个匿名的 closure object,其类型叫 closure type
      • std::bind(...) 返回对象,更准确叫bind expression object。
      • std::function<R(Args...)> 对象,更准确叫 type-erased polymorphic function wrapper。通常也算functor。
      • C++20 CPO,Customization Point Object,即“定制点对象”。例如std::ranges::begin。CPO 的重点不是“可调用”,而是“可定制”。所以 CPO 不只是“一个 functor”,它还承担了统一定制入口的角色。
      • C++20 Niebloid,用于描述 C++20 ranges 中的算法实体,例如std::ranges::sort。Niebloid 的关键特征包括:不能显式指定模板实参;不参与普通ADL

函数对象与函数指针相比,有两个优点:第一是编译器可以内联执行函数对象的调用;第二是函数对象内部可以保持状态。

函数式程序设计语言还支持闭包,例如,first-class函数支持在其创建时用到的函数外定义的变量的值保持下来,成为一个函数闭包。

C++函数对象的实例

编辑

传统的C/C++函数指针:

#include <stdlib.h>

/* Callback function, returns < 0 if a < b, > 0 if a > b, 0 if a == b */
int compareInts(const void* a, const void* b)
{
  return *(const int *)a - *(const int *)b;
}
...
// prototype of qsort is
// void qsort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *));
...
int main(void)
{
    int items[] = { 4, 3, 1, 2 };
    qsort(items, sizeof(items) / sizeof(items[0]), sizeof(items[0]), compareInts);
    return 0;
}

C++中,函数对象是定义了函数调用运算符的类对象,称作class type functor

// comparator predicate: returns true if a < b, false otherwise
struct IntComparator
{
  bool operator()(const int &a, const int &b) const
  {
    return a < b;
  }
};
...
// An overload of std::sort is:
template <class RandomIt, class Compare>
void sort(RandomIt first, RandomIt last, Compare comp);
...
int main()
{
    std::vector<int> items { 4, 3, 1, 2 };
    std::sort(items.begin(), items.end(), IntComparator());
    return 0;
}

除了类类型函数对象,还有其他类型的函数对象,如使用成员函数指针或模板。C++11允许使用具有闭包功能的匿名函数。

C++ STL中的函数对象

编辑

C++的STL中的众多algorithm,非常依赖于函数对象处理容器的元素。因此,STL预定义了许多函数对象、谓词(predicate)、以及用于复合(composite)函数对象的binder、member function adapter、 pointer to function adapters、 negaters、 function objects base structure。由于STL中的algorithm使用函数对象作为参数时,一般都是传值调用,所以函数对象应该仔细设计其复制构造函数

预定义的函数对象

编辑

C++98在头文件functional中定义了下述函数对象:

  • plus<type>() 结果为(param1 + param2)
  • minus<type>() 结果为(param1 - param2)
  • multiplies<type>() 结果为(param1 * param2)
  • divides<type>() 结果为(param1 / param2)
  • modulus<type>() 结果为(param1 % param2)

谓词(predicate)

编辑

返回布尔值(或者可以隐式转换为布尔值)的函数对象。用于STL中的algorithm时,谓词应该是无状态的( stateless)函数对象,即谓词的结果不依赖于内部的数据成员。这是因为STL中的algorithm不保证内部实现时对传入的谓词要复制多少次。 C++98在头文件functional中定义了下述谓词:

  • equal_to<type>() 结果为(param1 == param2)
  • not_equal_to<type>() 结果为(param1 != param2)
  • less<type>() 结果为 (param1 < param2)
  • greater<type>() 结果为(param1 > param2)
  • less_equal<type>() 结果为 (param1 <= param2)
  • greater_equal<type>() 结果为 (param1 >= param2)
  • logical_not<type>() 结果为 (!param1)
  • logical_and<type>() 结果为 (param1 && param2)
  • logical_or<type>() 结果为 (param1 || param2)

Function Adapter

编辑

用于组合(combine)、变换(transform)、操作(manipulate)函数对象、特定参数值、或者特定函数。进一步细分为:

Binder

编辑

C++98在头文件functional中定义了两个函数bind1st与bind2nd,返回值为binder1st、binder2nd类型。用于把二元函数对象分别绑定第一个、第二个参数后成为单元函数对象。

Negater

编辑

negate把一个作为谓词的函数对象取反。C++98在头文件functional中定义了两个函数not1与not2,返回值为unary_negate、binary_negate类型。

Member function adapter

编辑

Member function adapter用于把类的成员函数用作STL中的algorithm的参数。C++98在头文件functional中定义了:

  • 函数mem_fun,返回值为mem_fun_t类型,用于通过一个类对象指针来调用成员函数指针
  • 函数mem_fun_ref,返回值为mem_fun_ref_t类型,用于通过一个类对象引用来调用成员函数指针。

Pointer to function adapter

编辑

函数指针适配器(Pointer to function adapter)是把函数指针包装为一个函数对象,以便STL中的algorithm用函数对象作为统一的参数类型,不用再考虑以函数指针作为传入参数的情形。C++98在头文件functional中定义了:

  • 函数ptr_fun,返回值为pointer_to_unary_function类型,包装了一个单参数的函数指针。
  • 重载函数ptr_fun,返回值为pointer_to_binary_function类型,包装了一个双参数的函数指针。

Function Object Base

编辑

函数对象基类(Function Object Base)定义在头文件functional中,用作STL的预定义的与函数对象有关的各个类的基类,其中定义了几个类型,分别表示函数调用的各个参数类型、结果类型。

  • unary_function类,定义了2个类型:argument_type、result_type;
  • binary_function类,定义了3个类型:first_argument_type、second_argument_type、result_type;

C#

编辑

C#的函数对象是通过委托(delegate)声明的。

using System;
using System.Collections.Generic;

static int CompareFunction(int x, int y)
{
    return x - y;
}


List<int> items = new(4, 3, 1, 2);
Comparison<int> del = CompareFunction;
items.Sort(del);


using System;
using System.Collections.Generic;

List<int> items = new(4, 3, 1, 2);
items.Sort((x, y) => x - y);

JavaScript

编辑

JavaScript中,函数是头等对象,并且支持闭包。

function Accumulator(start) {
  let current = start
  return function (x) {
    return current += x
  }
}

用法:

let a = Accumulator(4)
let x = a(5)   // x has value 9
x = a(2)       // x has value 11

let b = Accumulator(42)
x = b(7)       // x has value 49 (current = 49 in closure b)
x = a(7)       // x has value 18 (current = 18 in closure a)

PowerShell

编辑

Windows PowerShell语言中,脚本块(script block)是可作为一个整体单元使用的语句集合或表达式集合。脚本块可以接收参数并返回值。脚本块是Microsoft .NET Framework类型System.Management.Automation.ScriptBlock的实例。

Function Get-Accumulator($x) {
    {
        param($y)
        return $x += $y
    }.GetNewClosure()
}
PS C:\> $a = Get-Accumulator 4
PS C:\> & $a 5
9
PS C:\> & $a 2
11
PS C:\> $b = Get-Accumulator 32
PS C:\> & $b 10
42

Python

编辑

Python程序设计中,函数是作为头等对象(first-class object),可以如同普通的字符串、数值、list等对象那样操作。这使得大部分编写函数对象都是不必须的。

任何对象定义了__call__()方法,就具有了可以当作函数调用的语法。例如下面的做累加的类[2]

class Accumulator(object):
    def __init__(self, n):
        self.n = n
    def __call__(self, x):
        self.n += x
        return self.n

一个使用例子:

 >>> a = Accumulator(4)
 >>> a(5)
 9
 >>> a(2)
 11
 >>> b = Accumulator(42)
 >>> b(7)
 49

因为函数就是对象,可当作局部变量定义、确定其属性(内部状态)、作为别的函数的返回值。[3]例如:

def Accumulator(n):
    def inc(x):
        inc.n += x
        return inc.n
    inc.n = n
    return inc

Python 3中,可以用函数闭包来创建函数对象:

def Accumulator(n):
    def inc(x):
        nonlocal n
        n += x
        return n
    return inc

注释

编辑
  1. ^ In C++, a functionoid is an object that has one major method, and a functor is a special case of a functionoid.[1] They are similar to a function object, but not the same.

参考文献

编辑
  1. ^ 33.15: What's the difference between a functionoid and a functor? 互联网档案馆存檔,存档日期2004-10-13.
  2. ^ Accumulator Generator. [2013-08-03]. (原始内容存档于2020-11-09). 
  3. ^ Python reference manual - Function definitions. [2013-08-03]. (原始内容存档于2020-12-15). 

进一步阅读

编辑
  • David Vandevoorde & Nicolai M Josuttis (2006). C++ Templates: The Complete Guide, ISBN 0-201-73484-2: Specifically, chapter 22 is devoted to function objects.

外部链接

编辑

📚 Artikel Terkait di Wikipedia

C++ Technical Report 1

多形態的函式包裝器(Polymorphic Function Wrapper) function 基於Boost.Function 儲存任何使用特定函式簽名的"可呼叫物"(函数指针、成員函式指针、仿函数),不需要可呼叫物確切的型別。 仿函数綁定器(Function Object Binders)

单子 (函数式编程)

{\displaystyle M:{\mathit {Val}}\to {\mathit {Val}}} While a (parametrically polymorphic) function in programming terms, unit (often called η in category theory) is

多态 (计算机科学)

ISSN 0360-0300. doi:10.1145/6041.6042. (原始内容 (PDF)存档于2019-10-14). : "Polymorphic types are types whose operations are applicable to values of more than

C++11

1402(页面存档备份,存于互联网档案馆): Doug Gregor(2002年10月22日)A Proposal to add a Polymorphic Function Object Wrapper to the Standard Library ^ Doc No. 1403(页面存档备份,存于互联网档案馆):

A+

→ VisualAPL ELI(英语:ELI (programming language)) GNU APL J K Q PPL(英语:Polymorphic Programming Language) 社群 协会 计算机协会:SIGAPL 英国APL协会 组织 商业 Analogic公司 CompuServe

CYP2D6

doi:10.1111/j.1527-3458.2002.tb00221.x.  Yu AM, Idle JR, Gonzalez FJ. Polymorphic cytochrome P450 2D6: humanized mouse model and endogenous substrates

APL語言

数学表示法 影響語言 A和A+ FP J K LYaPAS(英语:LYaPAS) MATLAB Nial(英语:Nial) PPL(英语:Polymorphic Programming Language) S Speakeasy(英语:Speakeasy (computational environment))

J语言

J语言最初起步于肯尼斯·艾佛森在1987年发表的《APL字典》,它实现了其中至关重要的秩的概念。J语言提供隐式定义机制包括秩、钩子、叉子和多种函数复合(英语:function composition (computer science)),并介入了作为头等对象的动名词,用以建立控制结构,它常被作为隱式編程的典范之一。