谓词函数、函数对象

从概念上讲,函数对象用作函数的对象;但是从实现上来说,函数对象时实现了 operate()的类的对象。
虽然函数和函数指针也可以归为函数对象,但实现了operate()的类的对象才能保存状态,才能用于STL。

我们直接看定义:
一元函数:接受一个参数的函数,如f(x)。

一元谓词函数:如果一元函数返回一个BOOL类型的值,则该函数称为谓词。

二元函数:接受2个参数的函数,如f(x,y)。

二元谓词函数:如果二元函数返回一个BOOL值,则该函数称为二元谓词。

之所以给返回布尔类型的函数对象专门命名,是因为谓词是用来为算法判断服务的。

一元函数:

下面给个很简单的一元函数的例子:

template<typename elementType>
void FuncDispalyElement(const elementType& element)
{
    cout<<element<<endl;
}

该函数也可以采用另一种表现形式,即实现在包含在类或结构的operate()中:

template<typename T>
struct DispalyElememnt
{
    void opearator()(const T& elememnt) const
    {
        cout<<element<<endl;
    }
};

上面两种实现都可以用于STL算法for_each,将集合中的类容显示在屏幕上。

#include <algorithm>
#include <iostream>
#include <vector>
#include <list>

using namespace std;

// struct that behaves as a unary function
template <typename elementType>
struct DisplayElement
{
    void operator () (const elementType& element) const
    {
        cout << element << ‘ ‘;
    }
};

int main ()
{
    vector <int> vecIntegers;

    for (int nCount = 0; nCount < 10; ++ nCount)
        vecIntegers.push_back (nCount);

    list <char> listChars;
    for (char nChar = ‘a‘; nChar < ‘k‘; ++nChar)
        listChars.push_back (nChar);

    cout << "Displaying the vector of integers: " << endl;

    // Display the array of integers
    for_each ( vecIntegers.begin ()    // Start of range
          , vecIntegers.end ()        // End of range
          , DisplayElement <int> () ); // Unary function object

    cout << endl << endl;
    cout << "Displaying the list of characters: " << endl;

    // Display the list of characters
    for_each ( listChars.begin ()        // Start of range
          , listChars.end ()        // End of range
          , DisplayElement <char> () );// Unary function object

    return 0;
}

其中for_each方法接受三个参数,前两个分别制定范围的起点和终点,第3个指定对范围类的每个元素调用的函数,如对vector调用DispalyElement::operate().
虽然这里2中方法都可以,但是结构体更加强大,因为它除了拥有operate()之外,还可以拥有成员属性,下面对之前的一元函数稍作修改:

te<typename elementType>
struct DisplayElementKeepCount
{
   int Count;

   // Constructor
   DisplayElementKeepCount() : Count(0) {}

   // Display the element, hold count!
   void operator()(const elementType& element)
   {
      ++ Count;
      cout << element<< ‘ ‘;
   }
};

注意:operate()不再是const成员函数,因为它对成员Count进行递增,以记录自己被调用用于显示数据的次数,该计数是通过共有成员属性Count暴露的。下面是一个例子:

#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;

template<typename elementType>
struct DisplayElementKeepCount
{
   int Count;

   // Constructor
   DisplayElementKeepCount() : Count(0) {}

   // Display the element, hold count!
   void operator()(const elementType& element)
   {
      ++ Count;
      cout << element<< ‘ ‘;
   }
};

int main()
{
   vector<int> vecIntegers;
   for(int nCount = 0; nCount< 10; ++ nCount)
      vecIntegers.push_back(nCount);

   cout << "Displaying the vector of integers: "<< endl; 

   // Display the array of integers
   DisplayElementKeepCount<int> Result;
   Result = for_each( vecIntegers.begin()   // Start of range
                     , vecIntegers.end()        // End of range
                    // ,Result); //也可以用这行代替下一行
     , DisplayElementKeepCount<int>() );// function object

   cout << endl<< endl;

   // Use the state stores in the return value of for_each!
   cout << "‘"<< Result.Count<< "‘ elements were displayed!"<< endl;

   return 0;
}

注意这次试用了for_each的返回值。

一元谓词

知道了一元函数,一元谓词也就很好理解了,下面我么给个例子,然后将一元谓词用于std::find_if算法中:

structure as a unary predicate
template <typename numberType>
struct IsMultiple
{
   numberType Divisor;

   IsMultiple (const numberType& divisor)
   {
      Divisor = divisor;
   }

   bool operator () (const numberType& element) const
   {
      // Check if the dividend is a multiple of the divisor
      return ((element % Divisor) == 0);
   }
};

#include <algorithm>
#include <vector>
#include <iostream>
using namespace std; 

int main ()
{
   vector <int> vecIntegers;
   cout << "The vector contains the following sample values: ";

   // Insert sample values: 25 - 31
   for (int nCount = 25; nCount < 32; ++ nCount)
   {
      vecIntegers.push_back (nCount);
      cout << nCount << ‘ ‘;
   }
   cout << endl << "Enter divisor (> 0): ";
   int Divisor = 2;
   cin >> Divisor;

   // Find the first element that is a multiple of 4 in the collection
   auto iElement = find_if ( vecIntegers.begin ()
                      , vecIntegers.end ()
                      , IsMultiple<int>(Divisor) );  

   if (iElement != vecIntegers.end ())
   {
      cout << "First element in vector divisible by " << Divisor;
      cout << ": " << *iElement << endl;
    }

   return 0;
}

二元函数与二元谓词

与一元函数一元谓词一模一样,只是参数变为2个,下面给出一个二元谓词对字符串vector排序的例子。

#include <algorithm>
#include <string>
using namespace std;

class CompareStringNoCase
{
public:
   bool operator () (const string& str1, const string& str2) const
   {
     string str1LowerCase;

     // Assign space
     str1LowerCase.resize (str1.size ());

     // Convert every character to the lower case
     transform (str1.begin (), str1.end (), str1LowerCase.begin (), tolower);

     string str2LowerCase;
     str2LowerCase.resize (str2.size ());
     transform (str2.begin (), str2.end (), str2LowerCase.begin (),
     tolower);

     return (str1LowerCase < str2LowerCase);
   }
};

#include <vector>
#include <iostream>

template <typename T>
void DisplayContents (const T& Input)
{
   for(auto iElement = Input.cbegin() // auto, cbegin and cend: c++11
      ; iElement != Input.cend ()
      ; ++ iElement )
      cout << *iElement << endl;
}

int main ()
{
   // Define a vector of string to hold names
   vector <string> vecNames;

   // Insert some sample names in to the vector
   vecNames.push_back ("jim");
   vecNames.push_back ("Jack");
   vecNames.push_back ("Sam");
   vecNames.push_back ("Anna");

   cout << "The names in vector in order of insertion: " << endl;
   DisplayContents(vecNames);

   cout << "Names after sorting using default std::less<>: " << endl;
   sort(vecNames.begin(), vecNames.end());
   DisplayContents(vecNames);

   cout << "Names after sorting using predicate that ignores case:" << endl;
   sort(vecNames.begin(), vecNames.end(), CompareStringNoCase());
   DisplayContents(vecNames);

   return 0;
}

小结:

  一元谓词大量用于stl算法中,例如std::partition算法使用一元谓词来划分,stable_partition也使用一元谓词,还有诸如find_if()等查找,remove_if()等
删除元素的函数也使用一元谓词。对于二元谓词,同样也是,如删除响铃重复元素unique(),排序算法sort(),以及对2个范围进行操作的transform(),都需要二元谓词。谓词在stl中很有用途。

时间: 2024-10-26 13:45:42

谓词函数、函数对象的相关文章

javascript 函数与对象

javascript中的函数是非常重要的概念,也是比较难于理解的一个知识点! 下面就来聊聊函数: JS基于对象:什么是基于对象呢?简单的说所有代码都是"对象"; 比如函数: function person(){ } person();//1 调用函数; 比如变量: var  x=5; x=6     // 2 重新为x赋值; 上面的函数和变量都是对象:所有的函数都是Function类的对象,x是Number类的对象.相当于 var x=new Number(5): 注意: functi

python之attrgetter函数对对象排序

1 # 使用attrgetter函数对对象排序 3 # attrgetter处理对象,itemgetter处理序列 4 5 from operator import attrgetter 6 7 8 class user(): 9 def __init__(self, name, age): 10 self.name = name 11 self.age = age 12 13 def __repr__(self): 14 '''定义该对象需要输出时的字符串格式''' 15 return sel

JavaScript学习总结(二)——闭包、IIFE、apply、函数与对象

目录 一.闭包(Closure) 1.1.闭包相关的问题 1.2.理解闭包 二.对象 2.1.对象常量(字面量) 2.2.取值 2.3.枚举(遍历) 2.4.更新与添加 2.5.对象的原型 2.6.删除 2.7.封装 三.函数 3.1.参数对象 (arguments) 3.2.构造函数 3.3.函数调用 3.3.1.call 3.3.2.apply 3.3.3.caller 3.3.4.Callee 3.5.立即执行函数表达式 (IIFE) 3.5.1.匿名函数与匿名对象 3.5.2.函数与函数

2016.8.07 一个自欺欺人的代码(便于理解函数和对象)

之前在对象基础里,我说对象就像游戏里的一个人物,这个人物有血条,魔条,物理攻击力等等的基本属性,也有攻击等基本方法. 在这里,我就写一个小例子,是用来自欺欺人的.真的,随便玩玩就好,如果想认真的做一个回合制游戏,可以参考我这个代码. 不说了,直接上代码: function Person(o){ this.name= o.name; //名字 this.profession= o.profession;//职业 this.rank= o.rank; //等级 this.blood= o.blood

用工厂函数封装对象

工厂函数是在方法中建立一个Object空对象,然后再对此对象添加属性和方法,然后把Object对象返回 <html> <head> <title>工厂函数封装对象</title> <script type="text/javascript"> // 工厂函数模式封装对象 //特点:返回结果为对象的函数,在用工厂函数创建对象的时候,一定要new function fn(nianling){ var obj=new object(

JS函数和对象(一)

在本文章中,将对JS中的函数和对象进行一些讲解,不对之处还请之处 一.JS中的函数 1.1无参函数 其形式如下代码所示 function box(){ alert("我是一个函数,只有被调用才会执行"); } box(); 运行结果如图1.1 图1.1 1.2有参函数 有参函数其形式如下 function box(name,age){ alert("你的名字是"+name+",年龄为"+age); } box("李大宝",22

avaScript学习总结(二)——闭包、IIFE、apply、函数与对象 目录

一.闭包(Closure) 1.1.闭包相关的问题 请在页面中放10个div,每个div中放入字母a-j,当点击每一个div时显示索引号,如第1个div显示0,第10个显示9:方法:找到所有的div,for循环绑定事件. 示例代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>闭包</title> <style type="

1,对象的存储细节,2,#pragma mark指令,3,函数和对象方法的区别,4,对象和方法之间的关系 ,5.课堂习题

1,对象的存储细节, 1. 当创建一个对象的时候:Person *p1 = [Person new],做了三件事情: 1,申请堆内存空间: 2,给实例变量初始化: 3,返回所申请空间的首地址; 2. 实例变量保存在堆区 3. 对象方法保存在代码区 4. 一个类可以创建多个对象: 2,#pragma mark指令, 功能:对代码分组,方便代码查找和导航 使用格式: #pragma mark - #waring 等待处理的功能,或者是未完成的功能 3,函数和对象方法的区别, 一. 对象方法: -(v

理解javascript原型和作用域系列(2)——函数和对象的关系

上文(理解javascript原型和作用域系列(1)——一切都是对象)已经提到,函数就是对象的一种,因为通过instanceof函数可以判断. var fn = function () { }; console.log(fn instanceof Object); // true 对!函数是一种对象,但是函数却不像数组一样——你可以说数组是对象的一种,因为数组就像是对象的一个子集一样.但是函数与对象之间,却不仅仅是一种包含和被包含的关系,函数和对象之间的关系比较复杂,甚至有一点鸡生蛋蛋生鸡的逻辑

漫话javascript函数与对象的关系

javascript函数与对象的关系 一直觉得红宝书中的一句话特别经典"函数是对象,函数名是引用",揭示了函数就是对象的本质: 在javascirpt中有一类数据类型,我们通常称之为简单的数据类型,包含了常见的数字(Number),字符串(String),布尔值(true,false),以及常常让人难以琢磨和始料未及的null,undefined:除了这些之外的其他所有值我们都可以成为对象,虽然上述的几种简单的数据类型拥有方法或属性:但是他们不是可变的:在<javascript语