lantionzy

coding
posts - 10, comments - 39, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

泛型算法

Posted on 2009-10-15 09:42 lantionzy 阅读(1466) 评论(2)  编辑 收藏 引用 所属分类: C++ Primer

一、只读算法

    1、使用两个迭代器和一个值调用 find 函数,检查两个迭代器实参标记范围内的每一个元素。只要找到与给定值相等的元素,find 就会返回指向该元素的迭代器。如果没有匹配的元素,find 就返回它的第二个迭代器实参,表示查找失败。于是,只要检查该函数的返回值是否与它的第二个实参相等,就可得知元素是否找到了。
     int search_value = 42;
     // call find to see if that value is present
     vector<int>::const_iterator result = find(vec.begin(), vec.end(), search_value);
     // report the result
     cout << "The value " << search_value

          << (result == vec.end()? " is not present" : " is present")
          << endl;

    2、许多算法只会读取其输入范围内的元素,而不会写这些元素。find 就是一个这样的算法。另一个简单的只读算法是 accumulate,accumulate 带有三个形参。头两个形参指定要累加的元素范围。第三个形参则是累加的初值。

     // sum the elements in vec starting the summation with the value 42
     int sum = accumulate(vec.begin(), vec.end(), 42);

     // concatenate elements from v and store in sum
     string sum = accumulate(v.begin(), v.end(), string(""));

    3、find_first_of 函数。这个算法带有两对迭代器参数来标记两段元素范围,在第一段范围内查找与第二段范围中任意元素匹配的元素,然后返回一个迭代器,指向第一个匹配的元素。如果找不到元素,则返回第一个范围的 end 迭代器。假设 roster1 和 roster2 是两个存放名字的 list 对象,可使用 find_first_of 统计有多少个名字同时出现在这两个列表中:
     size_t cnt = 0;
     list<string>::iterator it = roster1.begin();
     // look in roster1 for any name also in roster2
     while   ((it = find_first_of(it, roster1.end(),roster2.begin(), roster2.end()))
               != roster1.end()) {
        ++cnt;
        // we got a match, increment it to look in the rest of roster1
        ++it;
     }
     cout << "Found " << cnt << " names on both rosters" << endl;

二、写容器元素的算法

    1、写入到输入序列的算法本质上是安全的——只会写入与指定输入范围数量相同的元素,fill 带有一对迭代器形参,用于指定要写入的范围,而所写的值是它的第三个形参的副本。执行时,将该范围内的每个元素都设为给定的值。如果输入范围有效,则可安全写入。这个算法只会对输入范围内已存在的元素进行写入操作。

     // reset each element to 0

     fill(vec.begin(), vec.end(), 0);    

     // set subsequence of the range to 10
     fill(vec.begin(), vec.begin() + vec.size()/2, 10);

    2、fill_n 函数带有的参数包括:一个迭代器、一个计数器以及一个值。该函数从迭代器指向的元素开始,将指定数量的元素设置为给定的值。fill_n 函数假定对指定数量的元素做写操作是安全的。初学者常犯的错误的是:在没有元素的空容器上调用 fill_n 函数(或者类似的写元素算法)。

     vector<int> vec; // empty vector
     // disaster: attempts to write to 10 (nonexistent) elements in vec
     fill_n(vec.begin(), 10, 0);

    3、确保算法有足够的元素存储输出数据的一种方法是使用插入迭代器。插入迭代器是可以给基础容器添加元素的迭代器。通常,用迭代器给容器元素赋值时,被赋值的是迭代器所指向的元素。而使用插入迭代器赋值时,则会在容器中添加一个新元素,其值等于赋值运算的右操作数的值。

      back_inserter 函数是迭代器适配器。与容器适配器一样,迭代器适配器使用一个对象作为实参,并生成一个适应其实参行为的新对象。本例中,传递给 back_inserter 的实参是一个容器的引用。back_inserter 生成一个绑定在该容器上的插入迭代器。在试图通过这个迭代器给元素赋值时,赋值运算将调用 push_back 在容器中添加一个具有指定值的元素。

     vector<int> vec; // empty vector
     // ok: back_inserter creates an insert iterator that adds elements to vec

     // appends 10 elements to vec
     fill_n (back_inserter(vec), 10, 0);

    4、向目标迭代器写入未知个数的元素。正如 fill_n 函数一样,目标迭代器指向存放输出数据的序列中第一个元素。这类算法中最简单的是 copy 函数。copy 带有三个迭代器参数:头两个指定输入范围,第三个则指向目标序列的一个元素。传递给 copy 的目标序列必须至少要与输入范围一样大。假设 ilst 是一个存放 int 型数据的 list 对象,可如下将它 copy 给一个 vector 对象:

     vector<int> ivec; // empty vector
    // copy elements from ilst into ivec
     copy (ilst.begin(), ilst.end(), back_inserter(ivec));

    5、有些算法提供所谓的“复制(copying)”版本。这些算法对输入序列的元素做出处理,但不修改原来的元素,而是创建一个新序列存储元素的处理结果。但不修改原来的元素,而是创建一个新序列存储元素的处理结果。replace 算法就是一个很好的例子。该算法对输入序列做读写操作,将序列中特定的值替换为新的值。该算法带有四个形参:一对指定输入范围的迭代器和两个值。每一个等于第一值的元素替换成第二个值。

     // replace any element with value of 0 by 42
     replace(ilst.begin(), ilst.end(), 0, 42);

     // create empty vector to hold the replacement
     vector<int> ivec;
     // use back_inserter to grow destination as needed

     //every element in ilst with the value 0 has the value 42 in ivec
     replace_copy (ilst.begin(), ilst.end(), back_inserter(ivec), 0, 42);

三、对容器元素重新排序的算法

    1、unique 算法带有两个指定元素范围的迭代器参数。该算法删除相邻的重复元素,然后重新排列输入范围内的元素,并且返回一个迭代器,表示无重复的值范围的结束。

     // sort words alphabetically so we can find the duplicates

     //words:the quick red fox jumps over the slow red turtle
     sort(words.begin(), words.end());
   
     vector<string>::iterator end_unique = unique(words.begin(), words.end());
     words.erase(end_unique, words.end());

    2、如果要删除重复的项,必须使用容器操作,在本例中调用 erase 实现该功能。这个函数调用从 end_unique 指向的元素开始删除,直到 words 的最后一个元素也删除掉为止。

    3、sort和stable_sort 算法,,stable_sort 保留相等元素的原始相对位置。sort 和 stable_sort 都是重载函数。其中一个版本使用元素类型提供的小于(<)操作符实现比较。在查找重复元素之前,我们就是用这个 sort 版本对元素排序。第二个重载版本带有第三个形参:比较元素所使用的谓词函数的名字。这个谓词函数必须接受两个实参,实参的类型必须与元素类型相同,并返回一个可用作条件检测的值。
     sort(words.begin(), words.end());

     // sort words by size, but maintain alphabetic order for words of the same size
     stable_sort(words.begin(), words.end(), isShorter);
    4、统计满足指定条件的项数。执行 count_if 时,首先读取它的头两个实参所标记的范围内的元素。每读出一个元素,就将它传递给第三个实参表示的谓词函数。此谓词函数。此谓词函数需要单个元素类型的实参,并返回一个可用作条件检测的值。count_if 算法返回使谓词函数返回条件成立的元素个数。

     //GT6 returns a bool value

     vector<string>::size_type wc = count_if(words.begin(), words.end(), GT6);

四、泛型算法中使用迭代器

    1、插入迭代器

       1.1 back_inserter,创建使用 push_back 实现插入的迭代器。

       1.2 front_inserter,使用 push_front 实现插入。该函数将创建一个迭代器,调用它所关联的基础容器的 push_front 成员函数代替赋值操作。只有当容器提供 push_front 操作时,才能使用 front_inserter。在 vector 或其他没有 push_front 运算的容器上使用 front_inserter,将产生错误。

       1.3 inserter,使用 insert 实现插入操作。这种适配器带有两个实参:所关联的容器和指示起始插入位置的迭代器。

       // position an iterator into ilst
       list<int>::iterator it =
                      find (ilst.begin(), ilst.end(), 42);
       // insert replaced copies of ivec at that point in ilst
       replace_copy (ivec.begin(), ivec.end(),
                   inserter (ilst, it), 100, 0);

       1.4 也许我们会认为可使用 inserter 和容器的 begin 迭代器来模拟 front_inserter 的效果。然而,inserter 的行为与 front_inserter 的有很大差别。在使用 front_inserter 时,元素始终在容器的第一个元素前面插入。而使用 inserter 时,元素则在指定位置前面插入。即使此指定位置初始化为容器中的第一个元素,但是,一旦在该位置前插入一个新元素后,插入位置就不再是容器的首元素了:

       list<int> ilst, ilst2, ilst3;

       // after this loop ilst contains: 3 2 1 0
       for (list<int>::size_type i = 0; i != 4; ++i)
          ilst.push_front(i);
       // after copy ilst2 contains: 0 1 2 3
       copy (ilst.begin(), ilst.end(), front_inserter(ilst2));
       // after copy, ilst3 contains: 3 2 1 0
       copy (ilst.begin(), ilst.end(), inserter (ilst3, ilst3.begin()));

    2、iostream 迭代器

       虽然 iostream 类型不是容器,但标准库同样提供了在 iostream 对象上使用的迭代器: istream_iterator用于读取输入流,而ostream_iterator则用于写输出流。这些迭代器将它们所对应的流视为特定类型的元素序列。使用流迭代器时,可以用泛型算法从流对象中读数据(或将数据写到流对象中)。

       2.1 可使用 istream_iterator 对象将标准输入读到 vector 对象中。

       istream_iterator<int> in_iter(cin); // read ints from cin

       istream_iterator<int> eof; // istream "end" iterator
      // read until end of file, storing what was read in vec
      while (in_iter != eof)
             // increment advances the stream to the next value
             // dereference reads next value from the istream
           vec.push_back(*in_iter++);

       2.2 可使用 ostream_iterator 对象将一个值序列写入流中,其操作的过程与使用迭代器将一组值逐个赋给容器中的元素相同:

       // write one string per line to the standard output
      ostream_iterator<string> out_iter(cout, "\n");
      // read strings from standard input and the end iterator
      istream_iterator<string> in_iter(cin), eof;
      // read until eof and write what was read to the standard output
      while (in_iter != eof)
         // write value of in_iter to standard output
         // and then increment the iterator to get the next value from cin
          *out_iter++ = *in_iter++;

       2.3 流迭代器d的几个重要限制:

           不可能从 ostream_iterator 对象读入,也不可能写到 istream_iterator 对象中。

           一旦给 ostream_iterator 对象赋了一个值,写入就提交了。赋值后,没有办法再改变这个值。此外,ostream_iterator 对象中每个不同的值都只能正好输出一次。

           ostream_iterator 没有 -> 操作符。
    3、反向迭代器
       反向迭代器是一种反向遍历容器的迭代器。也就是,从最后一个元素到第一个元素遍历容器。反向迭代器将自增(和自减)的含义反过来了:对于反向迭代器,++ 运算将访问前一个元素,而 -- 运算则访问下一个元素。

       // reverse iterator of vector from back to front
       vector<int>::reverse_iterator r_iter;
       for (r_iter = vec.rbegin(); // binds r_iter to last element
            r_iter != vec.rend();  // rend refers 1 before 1st element
            ++r_iter)              // decrements iterator one element
       cout << *r_iter << endl;    // prints 9,8,7,...0

    4、const 迭代器

       在之前使用 find 的程序中,我们将 result 定义为 const_iterator 类型。这样做是因为我们不希望使用这个迭代器来修改容器中的元素。find_first_of程序中也不打算改变容器内的任何元素,但是它却使用了普通的非 const 迭代器来保存 find_first_of 的返回值。

       原因是,在第二个例子中,程序将迭代器用作 find_first_of 的实参:

       find_first_of(it, roster1.end(), roster2.begin(), roster2.end())

       该函数调用的输入范围由 it 和调用 roster1.end() 返回的迭代器指定。算法要求用于指定范围的两个迭代器必须具有完全一样的类型。roster1.end() 返回的迭代器依赖于 roster1 的类型。如果该容器是 const 对象,则返回的迭代器是 const_iterator 类型;否则,就是普通的 iterator 类型。在这个程序中,roster1 不是 const 对象,因而 end 返回的只是一个普通的迭代器。

    5、迭代器分类

       Input iterator(输入迭代器)              读,不能写;只支持自增运算

       Output iterator(输出迭代器)             写,不能读;只支持自增运算

       Forward iterator(前向迭代器)            读和写;只支持自增运算

       Bidirectional iterator(双向迭代器)      读和写;支持自增和自减运算

       Random access iterator(随机访问迭代器)  读和写;支持完整的迭代器算术运算

 

Feedback

# re: 泛型算法  回复  更多评论   

2009-10-15 11:20 by 淘宝导购
不错哦

# re: 泛型算法  回复  更多评论   

2009-10-15 14:08 by lantionzy
@淘宝导购
欢迎评论和交流

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理