引子
雨晨跟小艾拍拖都快两年了,终于到了要见家长的关头;雨晨相当紧张,因为第一印象相当重要,尤其体现在穿着打扮上,毕竟“人靠衣装,佛靠金装”么。小艾非常体谅雨晨的心情,于是拉他到一家男装超市(就当真的有这种超市吧),血拼开始:)
深谙自家父母的品位的小艾提出了这样的选购方案:
1) 要求商家将所有的上衣编号为1, 2, 3, ... , m;所有类别的裤子编号为 1, 2, ..., n;所有类别的鞋子标号为1, 2, ..., o; (---- 假设有这样耐心的商家)
2) 要求雨晨依次从上衣、裤子和鞋子中选取一件,全部穿上后由小艾进行打分 (---- 假设有这样温驯的雨晨)
3) 选购分数最高的组合
雨晨是个程序员,听完这个方案后,出于职业习惯,脑海里很快浮现出这样的伪代码:
1 max_score = 0;
2 choice = choice (0, 0, 0 );
3 for ( size_t i = 0; i < m; ++i )
4 for ( size_t j = 0; j < n; ++j )
5 for ( size_t k = 0; k < o; ++k )
6 {
7 current_score = score( clothes[i], trousers[j], shoes[k] );
8 if ( current_score > max_score )
9 {
10 max_score = current_score;
11 choice = choice( clothes[i], trousers[j], shoes[k] );
12 }
13 }
14 return choice;
两人刚商量好方案,旁听的售货员mm 就热心地推荐小艾在打分的时候考虑一下领带,衬衣, 袜子………… 的搭配,顺便提了一下她们店里的有 N 种男生服装饰品----她也不知道仓库里到底还有多少种 ---- 不过都是已经编号好了随时可以试用的;出于女人的天性,小艾欣然同意。
雨晨,假设雨晨还没有晕倒,还足够理智,脑海还能浮现出伪代码么?
iterator 模式
从程序员的角度来看,上边的问题可以简化为:
已知:
a) N 种男生服饰, N 大小在运行期决定
b) 每种服饰有多个款式, 具体款式数量在运行期决定
需求:
遍历所有服饰,找出所有组合
由于服饰的数量是未知,每种服饰的款式数量也是未知,非常直接的循环嵌套方法在这里很难用得上,需要另谋他路。
假设已经在运行时知道了服饰的数量是N,对应的数量分别是 m_1, m_2, ..., m_N,那么如何做出类似于直接手写的循环遍历呢?
考虑的所有组合的数量是 M = m_1 * m_2 * ... * m_N,如果能够用一种方式,将 数列 1, 2, 3, ..., M 依次映射到各种服饰的对应组合上,问题可迎刃而解。
于是有了 iterator 模式的实作:
1 #include <string>
2 #include <vector>
3 #include <map>
4 #include <cstddef>
5
6 struct finery_chooser
7 {
8 typedef std::string finery_identifier_type; // finery type, such as "clothes", "trousers", etc.
9 typedef std::string finery_style_type; // concret finery style, such as "Nike", "Jordan" ....... in "shoes"
10 typedef finery_style_type value_type;
11 typedef std::vector<value_type> value_arr_type;
12 typedef std::map<finery_style_type, value_arr_type> all_finery_identifer_style_associate_type;
13 typedef std::size_t size_type;
14
15 all_finery_identifer_style_associate_type& afisat;
16
17 struct iterator
18 {
19 typedef iterator self_type;
20 typedef finery_chooser::all_finery_identifer_style_associate_type all_finery_identifer_style_associate_type;
21 typedef finery_chooser::size_type size_type;
22 typedef finery_chooser::value_arr_type value_arr_type;
23 typedef finery_chooser::finery_identifier_type finery_identifier_type;
24 typedef size_type value_type;
25
26 all_finery_identifer_style_associate_type& afisat;
27 size_type n;
28
29 iterator( all_finery_identifer_style_associate_type& afisat_ ) : afisat( afisat_ ), n( 0 ) {}
30 iterator( all_finery_identifer_style_associate_type& afisat_, int ) : afisat( afisat_ ), n( 1 )
31 {
32 for ( auto i = afisat.begin(); i != afisat.end(); ++i )
33 { n *= ( *i ).second.size(); }
34 }
35
36 iterator( const self_type& other ) : afisat( other.afisat ), n( other.n ) {}
37
38 self_type&
39 operator ++ ()
40 {
41 ++n;
42 return *this;
43 }
44
45 const value_arr_type
46 operator * ()
47 {
48 value_type current_n = n;
49 value_arr_type ans;
50
51 for ( auto i = afisat.begin(); i != afisat.end(); ++i )
52 {
53 const finery_identifier_type id = ( *i ).first;
54 const size_type dim = ( *i ).second.size();
55 ans.push_back( ( *i ).second[current_n%dim] );
56 current_n /= dim;
57 }
58
59 return ans;
60 }
61
62 friend bool
63 operator != ( const self_type& lhs, const self_type& rhs )
64 {
65 return lhs.n != rhs.n;
66 }
67 };
68
69 finery_chooser( all_finery_identifer_style_associate_type& afisat_ ) : afisat( afisat_ ) {}
70
71 iterator begin()
72 {
73 return iterator( afisat );
74 }
75
76 iterator end()
77 {
78 return iterator( afisat, size_type() );
79 }
80
81 };
82
不过上边实作代码还缺少了一些标准 stl 迭代器定义的类型,比如pointer, reference, iterator_category 等等,还有 value_type 要修改为 value_arr_type,使用时可以根据需要自行修改添加。
外加一个典型的示例:
1 finery_chooser::all_finery_identifer_style_associate_type afisat;
2
3 //.......................................
4
5 finery_chooser fc( afisat );
6
7 for ( auto i = fc.begin(); i != fc.end(); ++i )
8 {
9 int current_score = score( *i );
10 //............................................
11 }
或者直接:
1 std::for_each( fc.begin(), fc.end(), what_func_you_want);
讨论
通过上边示例代码可以看出,使用 iterator 模式的好处是摆脱了烦冗的循环书写,对于任何习惯 stl 迭代器习惯用法的人来说,代码的表达意思是相当清晰的。
缺点也有,比如实作代码相比传统的嵌套循环表达方式可读性相当差,比如如此抽象成 iterator 模式代码的表达力度并不总是强于传统方式,等等……
实作代码是对引子中的故事量身定制的,这么强的类型耦合其实是没有必要的,范化这段代码的工作就作为练习吧~~~~
多层循环的遍历当然可以有更优雅的表达方式,某已抛砖引玉,静候楼下诸位高论。