谓词函数、函数对象

从概念上讲,函数对象用作函数的对象;但是从实现上来说,函数对象时实现了 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中很有用途。

posted @ 2016-12-23 21:50  任智康  阅读(1914)  评论(0编辑  收藏  举报