• 仿函数应用详解


    仿函数(Functors,Function Objects)

    定义:

    我们都清楚普通函数的定义与使用方法,我们可以说任何东西,只要其行为像函数,它就是个函数。如果我们定义了一个对象,其行为像函数,它就可以被当函数来使用。所谓函数行为,是指可以“使用小括号传递参数,蔚以调用某个东西”。例如:

    function(arg1, arg2);//a function call

    所谓仿函数,又叫函数对象,是一个定义operator()的对象。例如:

    FunctionObjecType FunctorObj;
    ...
    FunctorObj(...);
    其中表达式FunctorObj()将会调用FunctorObj的operator()实现,即进行了操作符的重载,而非调用FunctorObj()。

    函数对象概念包括Generator, Unary Function(一元函数), 和Binary Function(二元函数),分别可以f(),f(x),f(x, y)的形式调用。返回bool类型的仿函数比较特殊,比如返回bool的Unary Function叫做Predicate(判断式),返回bool的Binary Function叫做Binary Predicate。


    Predicate就经常应用于STL算法中,一些算法可以接受用户定义的的辅助函数,提高了算法的灵活性。比较经常应用的场景就是指定排序准则和搜寻准则。


    一个类的使用看上去一个函数,它是通过通过重载类的()操作符来实现的。如下

    例子1

    class CPrintInt
    {
    public:
    	void operator()(int nElements)const//使用重载()来实现
    	{
    		cout <<"call operator():"<< nElements << endl;
    	}
    };
    
    CPrintInt CPrint;
    
    CPrint(10);//像函数一样去使用
    CPrint.operator()(10);//显示调用

    优势

    仿函数相对于普通的函数,具有更加复杂的代码设计,然而仿函数也有其过人之处,它具有如下的优点:
    1、拥有状态
    仿函数的能力超越了operator(),仿函数可以拥有成员函数和成员变量,这就意味着仿函数可以同时拥有状态不同的两个实体。一般函数则达不到这样的能力。
    2、拥有自己的型别
    我们可以将仿函数的型别当做template参数来传递,从而指定某种行为模式。容器型别也会因为仿函数的不同而不同。
    3、仿函数速度更快
    就template概念而言,由于很多细节在编译期就已经确定,传入一个仿函数,就可能活动更好的性能。

    仿函数应用

    排序准则(Sort Criteria)

    我们有时候会遇到需要将object 以已序(sorted) 形式置于容器中,此时仿函数就可以派上用场了。如下:
    例子2
    class PersonInfo
    {
    public:
        PersonInfo(const string&strFirstName, const string& strSecondName)
        {
            m_strFirstName = strFirstName;
            m_strSecondName = strSecondName;
        }
        string& GetFirstName()
        {
            return m_strFirstName;
        }
        string& GetSecondName()
        {
            return m_strSecondName;
        }
    public:
        string m_strFirstName;
        string m_strSecondName;
    };
    
    class PersonSortCriterion
    {
    public:
        bool operator()(const PersonInfo& Per1, const PersonInfo &Per2)
        {
            //a person is less than another person
            //if the second name is less
            //if the second name is equal and the first name is less
            return ((Per1.m_strSecondName < Per2.m_strSecondName) ||
                    (!(Per2.m_strSecondName < Per1.m_strSecondName) &&
                    Per1.m_strFirstName < Per2.m_strFirstName));
        }
    };
    
    /****************************************************************
    *函数名称:FunctorSortCriteria
    *功    能:仿函数应用于排序准则
    *作    者:Jin
    *日    期:2016年6月17日
    ****************************************************************/
    void FunctorSortCriteria()
    {  
        typedef set<PersonInfo, PersonSortCriterion> PersonSet;
    
        PersonSet PersonNamesInfo;
        PersonInfo p1("xu", "jin");
        PersonInfo p2("huang", "jin");
        PersonInfo p3("hua", "tong");
        PersonInfo p4("huang", "shu");
        PersonInfo p5("xu", "wei");
    
        PersonNamesInfo.insert(p1);
        PersonNamesInfo.insert(p2);
        PersonNamesInfo.insert(p3);
        PersonNamesInfo.insert(p4);
        PersonNamesInfo.insert(p5);
        
        //print user define sort criterion
        PersonSet::iterator it = PersonNamesInfo.begin();
        for (; it != PersonNamesInfo.end(); it++)
        {
            cout << "FirtName:"    << it->GetFirstName() << "	"
                 << "SecondName: " << it->GetSecondName() 
                 << endl;
        }  
    }

    运行结果:


    拥有内部状态(Internal State)

    这个例子以仿函数产生一个序列,初始值可以通过仿函数的构造函数提供。同时仿函数是passed by valued(传值)
    ,不是passed 不用reference(传址),我们所改变的是仿函数的副本。

    例如3
    /****************************************************************
    *函数名称:HoldStateByValue
    *功    能:模拟仿函数在同一个时刻下拥有多个状态(多个函数实体)
    *作    者:Jin
    *日    期:2016年6月5日
    ****************************************************************/
    void HoldStateByValue()
    {
    	list<int> Coll;
        //Note:make sure container has enough capcity or use insert itertor 
        //    when you are using generate_n or generat algorithm
    
    	//insert value from 1 to 9,
    	generate_n(back_inserter(Coll), //start
    			   9,                   //number of element
                   IntSequence(1));     //generator values,initial value 1 functor obj
    
        //output: 1 2 3 4 5 6 7 8 9 
        PrintElements(Coll);
        
        //replace second to last element,starting at 42
        generate(++Coll.begin(), 
                 --Coll.end(),
                 IntSequence(42)); //initial value 42,functor obj
        //output:1 42 43 44 45 46 47 48 9
        PrintElements(Coll);
    
    }

    InitSequence seq(1);
    //insert value beginning with 1,by pass ,change internal state
    generate_n(back_inserter(coll), 9, seq)
    
    //insert value beginning with 1
    generate_n(back_inserter(coll), 9, seq)
    我们改变的是仿函数副本,不能获取对象最终状态,但是有个好处是我们可以传递常量或者暂时表达式,缺点就是不能得到“最终结果”或者“反馈”。现在有两种办法可以从中获取其“最终结果”。方式一是by reference ,方式二是运用for_each算法。
    方式一:
              以by reference的方式传递仿函数,我们只需要在调用算法时,明白标示仿函数是个reference型别即可。见例子4。
    例子4
    //Predefined algorithm is not by reference, so user defines it
    // generate_n<back_insert_iterator<list<int> >,
    //            int,
    //            IntSequence&>(back_inserter(Coll), 4, SeqValue);
    template<class _OutIt, class _Diff, class _Fn0>
    void GenSequenceUser(_OutIt _Dest, _Diff _Count, _Fn0 &_Func)//by reference
    {
        for (; 0 < _Count; --_Count, ++_Dest)
        {
            *_Dest = _Func();
        }
    }
    /****************************************************************
    *函数名称:HoldStateByReference
    *功    能:仿函数的by reference特性
    *作    者:Jin
    *日    期:2016年6月5日
    ****************************************************************/
    void HoldStateByReference()
    {
        list<int> Coll;
        IntSequence SeqValue(1);
        
        //function object By reference ,so m_nValue will continue with 6 in the SeqVale
        //insert 1 2 3 4 5
        GenSequenceUser(back_inserter(Coll), 5, SeqValue);
        //first output:1 2 3 4 5 
        PrintElements(Coll, "first time insert value: ");
    
        //initial m_nValue is 6,so will insert 6 7 8 9 10
        generate_n(back_inserter(Coll), 5, SeqValue);
        //second output:1 2 3 4 5  6 7 8 9 10
        PrintElements(Coll, "second time insert value: ");
    
        //function object By pass, so that m_nValue initial value doesn't change,it is 6 ;
        //insert 6 7 8 9 10
        generate_n(back_inserter(Coll), 5, SeqValue);
        //output: 1 2 3 4 5  6 7 8 9 10 6 7 8 9 10
        PrintElements(Coll ,"third time inset value: ");
            
        //creat other function object instance By pass, 
        //insert 20 21
        generate_n(back_inserter(Coll), 2, IntSequence(20));
        //output: 1 2 3 4 5  6 7 8 9 10 6 7 8 9 10 20 21
        PrintElements(Coll ,"fourth time inset value: ");
    }

    方式二:
    运用for_each()算法的返回值。如果我们使用了for_each()算法,我们可以得到for_each的返回值,来获取仿函数对象的最终状态,这是其他算法所不支持的,见例子5.
    例子5
    class CMeanValue
    {
    public:
        //constructor
        CMeanValue():lNum(0),lSum(0){};
        
        //function call
        void operator()(int elem)
        {
            lNum += 1;
            lSum += elem;
        }
    
        //return mean value
        double GetMeanValue()
        {
            return static_cast<double>(lSum) / static_cast<double>(lNum);
        }
    private:
        long lNum;//number of elements
        long lSum;//sum of all elements
    
    };
    /****************************************************************
    *函数名称:GetFunctorSatate
    *功    能:利用for_each返回值,获取仿函数结果
    *作    者:Jin
    *日    期:2016年6月5日
    ****************************************************************/
    void GetFunctorSatate()
    {
       list <int> Coll;
       const int nMaxNum= 10;
       for (int i = 0;i < nMaxNum; i++)
       {
           Coll.push_back(i);
       }
    
       CMeanValue obj;
       CMeanValue Ret;
       //for_each return Functor after deal all the elements
       Ret= for_each(Coll.begin(),Coll.end(), obj/*CMeanValue()*/);
       cout << "mean value: " << Ret.GetMeanValue() << endl;
    
    }

    仿函数(functor)在各编程语言中的应用

    实例:
    #include "stdafx.h"
    #include <string.h>
    #include <algorithm>
    #include <vector>
    #include <deque>
    #include <functional>
    #include <iostream>
    #include <list>
    #include <sstream>
    #include <iterator>
    #include <functional>
    #include <stdlib.h>
    
    using namespace std;
    
    /************************************************************************/
    /* 比较两个参数大小,确定排序规则                                        */
    /* 返回值:                                                             */
    /*     > 0     a排在b后面                                               */
    /*     = 0     排序不确定                                               */
    /*     < 0     a排在b前面                                               */
    /************************************************************************/
    int CompareAge(const void *a, const void *b)
    {
    	return *((int*)(a)) - *((int*)(b));
    }
    
    //define less function object
    class CUseLess
    {
    public:
    	bool operator()(int a, int b)const
    	{
    		return a < b;
    	}
    };
    
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	const int MaxNum = 5;
    	int nAge[MaxNum] = {10, 12, 30, 4, 27};
    
    	//C语言使用函数指针和回调函数来实现仿函数
    	printf("Use function point and Callback function
    ");
    	qsort(nAge, sizeof(nAge)/sizeof(0), sizeof(int), CompareAge);
    	for (int i = 0; i < MaxNum; i++)
    	{
    		printf("%d ",nAge[i]);
    	}
    	printf("
    ");
    
    	//C++中,使用在一个类中重载括号运算符的方法,使一个类对应具有函数行为
    	vector <int> vecCollector;
    	for (int i = 0; i < MaxNum; ++i)
    	{
    		vecCollector.push_back(nAge[i]);
    	}
    
    	//其实sort排序算法中,我们经常会引用“functional”中预定义的仿函数,
    	//诸如less,greater,greater_equal等,这里只是使用用户定义版的less,实现原理是一样的。
    	//sort(vecCollector.begin(), vecCollector.end(), less<int>());//functional中的仿函数
    	cout << "Use Fucntion object" << endl;
    	sort(vecCollector.begin(), vecCollector.end(), CUseLess());	//用户自定义的仿函数
    	copy(vecCollector.begin(), vecCollector.end(), ostream_iterator<int>(cout," "));
    	cout << endl;
    	return 0;
    }

    输出:

    预定义的仿函数

          STL中提供了许多预定义的仿函数,要使用这些预定义的仿函数必须包含头文件<functional>,,对于对对象排序或进行比较时,一般都是以less<>为预设准则。表1列出了这些仿函数。

  • 相关阅读:
    HipHop PHP & HHVM资料收集
    [转]Linux系统下如何查看及修改文件读写权限
    [转]Console命令详解,让调试js代码变得更简单
    js中(function(){…})()立即执行函数写法理解
    [Link]NoSQL
    [转]Hadoop Hive sql语法详解
    [转]redis配置文件redis.conf的详细说明
    【转】各种 NoSQL 的比较
    [转]MongoDB基本使用
    【转】windows下mongodb安装与使用整理
  • 原文地址:https://www.cnblogs.com/jinxiang1224/p/8468430.html
Copyright © 2020-2023  润新知