最近看了一些关于编程范式的文章,简要做一些小结和记录 在现实生活中,为了适配各种规格的螺帽,我们需要许多种类的螺丝刀。 在编程世界中,静态语言有许多种类的数据类型。 不过,我们可以发现,无论是传统世界,还是编程世界,我们都在干一件事情,就是通过使用一种更为通用的方式,抽象和隔离,让复杂的“世界”变得简单一些。 原版,swap交换变量(只能交换int型) void swap(int* x, int* y) { int tmp = *x; *x = *y; *y = tmp; } 改进版,使用void * 抽象化数据类型,范式编程: void swap(void* x, void* y, size_t size) { char tmp[size]; memcpy(tmp, y, size); memcpy(y, x, size); memcpy(x, tmp, size); } 函数接口中增加了一个size参数。一旦用了 void* 后,类型就会被“抽象”掉,编译器不能通过类型得到类型的尺寸了,所以需要我们手动加上一个类型长度的标识。 函数的实现中使用了memcpy()函数。因为类型被“抽象”掉了,所以不能用赋值表达式了,很有可能传进来的参数类型还是一个结构体,不过,为了要交换这些复杂类型的值,我们只能使用内存复制的方法了。 函数的实现中使用了一个temp[size]数组。这就是交换数据时需要用的 buffer,会用 buffer 来做临时的空间存储。 原版C语言函数,搜索target在整型数组中的位置: int search(int* a, size_t size, int target) { for(int i=0; iname, y->name); if (n != 0) return n; return strcmp(x->id, y->id); } 上面的泛型search函数缺陷:只支持顺序类型的数据结构,遇到复杂的图、树等无法抽象化非顺序型的数据容器 另外C语言还可以使用宏定义来泛型化。 一个良好的泛型编程需要解决如下几个泛型编程的问题: 1.算法的泛型; 2.类型的泛型; 3.数据结构(数据容器)的泛型。 就像前面的search()函数,里面的 for(int i=0; i Iter search(Iter pStart, Iter pEnd, T target) { for(Iter p = pStart; p != pEnd; p++) { if ( *p == target ) return p; } return NULL; } 在 C++ 的泛型版本中,我们可以看到: 使用typename T抽象了数据结构中存储数据的类型。 使用typename Iter,这是不同的数据结构需要自己实现的“迭代器”,这样也就抽象掉了不同类型的数据结构,迭代器需要数据结构自己去实现。 然后,我们对数据容器的遍历使用了Iter中的++方法,这是数据容器需要重载的操作符,这样通过操作符重载也就泛型掉了遍历, 为了兼容原有 C 语言的编程习惯我们不用标准接口Iter.Next(),不用Iter.GetValue()来取代*。 在函数的入参上使用了pStart和pEnd来表示遍历的起止。 使用Iter来取得这个“指针”的内容。这也是通过重载 取值操作符来达到的泛型。 说明:所谓的Iter,在实际代码中,就是像vector::iterator或map::iterator这样的东西。这是由相应的数据容器来实现和提供的(迭代器)。 C++ STL源码中的find函数 template InputIterator find (InputIterator first, InputIterator last, const T& val) { while (first!=last) { if (*first==val) return first; ++first; } return last; } C语言版求和函数 long sum(int *a, size_t size) { long result = 0; for(int i=0; i T sum(Iter pStart, Iter pEnd) { T result = 0; for(Iter p=pStart; p!=pEnd; p++) { result += *p; } return result; } 这里默认了 T result = 0;也就是T假设了 Iter 中出来的类型是T。0假设了类型是int;如果类型不一样,就会导致转型的问题 改进版,需要迭代器 Iter在实际调用者那会是一个具体的像vector::iterator的东西 在这个声明中,int已经被传入Iter中了;所以定义result的T应该可以从Iter中来。这样就可以保证类型是一样的,而且不会有被转型的问题。 template class container { public: class iterator { public: typedef iterator self_type; typedef T value_type; typedef T* pointer; typedef T& reference; reference operator*(); pointer operator->(); bool operator==(const self_type& rhs); bool operator!=(const self_type& rhs); self_type operator++() { self_type i = *this; ptr_++; return i; } self_type operator++(int junk) { ptr_++; return *this; } ... ... private: pointer _ptr; }; iterator begin(); iterator end(); ... ... }; 1.首先,一个迭代器需要和一个"数据容器"(类)在一起,因为里面是对这个容器的具体的代码实现,对这个容器的迭代。 2.它需要重载一些操作符,比如:取值操作*、成员操作->、比较操作==和!=,还有遍历操作++,等等。 3.然后,还要typedef一些类型,比如value_type,告诉我们容器内的数据的实际类型是什么样子。 4.还有一些,如begin()和end()的基本操作。 5.我们还可以看到其中有一个pointer _ptr的内部指针来指向当前的数据(注意,pointer就是 T*)。 有了迭代器,我们让用户自型传入模板T的类型,解决T result = 0出现的问题 最终Sum的范式写法: template typename Iter::value_type sum(Iter start, Iter end, T init) { typename Iter::value_type result = init; while (start != end) { result = result + *start; start++; } return result; } int main(){ container c; container::iterator it = c.begin(); sum(c.begin(), c.end(), 0); return 0; } 这就是整个 STL 的泛型方法,其中包括: 1.泛型的数据容器; 2.泛型数据容器的迭代器; 3.泛型的算法; 如果我们有一个 员工结构体,再想用sum函数来求和怎么办? struct Employee { string name; string id; int vacation; double salary; }; 结构体数组增加了很多数据类型,以前sum函数就不知道怎么办了吧, vector staff; //total salary or total vacation days? sum(staff.begin(), staff.end(), 0); 这个例子而言,我想计算员工薪水里面最高的,和休假最少的,或者我想计算全部员工的总共休假多少天。那么面对这么多的需求,我们是否可以泛型一些呢?怎样解决这些问题呢? 引入更抽象化的函数编程——reduce函数 template T reduce (Iter start, Iter end, T init, Op op) { T result = init; while ( start != end ) { result = op( result, *start ); //这里时重点 start++; } return result; } reduce函数 需要增加一个参数 op,这个参数可以是一个函数,来完成我们想要的业务操作。 比如下面的业务操作函数:我们来求员工的工资和、最大工资 double sum_salaries = reduce( staff.begin(), staff.end(), 0.0, {return s + e.salary;} ); double max_salary = reduce( staff.begin(), staff.end(), 0.0, {return s > e.salary? s: e.salary; } ); 下面这个示例中,先定义了一个函数对象counter(struct里定义函数)。这个函数对象需要一个Cond的函数对象,它是个条件判断函数,如果满足条件,则加 1,否则加 0。 然后,用上面的counter函数对象和reduce函数共同来打造一个counter_if算法 当条件满足的时候我就记个数,也就是统计满足某个条件的个数。 //对象counter,满足Cond函数的条件,就将参数c 加 1。 template struct counter { size_t operator()(size_t c, T t) const { return c + (Cond(t) ? 1 : 0); } }; //count_if函数,返回上面文章中编写使用的reduce函数 template size_t count_if(Iter begin, Iter end, Cond c){ return reduce(begin, end, 0, counter(c)); } 当我需要统计薪资超过 1 万元的员工的数量时,参数3为Cond条件函数,{ return e.salary > 10000; }一行代码就完成了 size_t cnt = count_if(staff.begin(), staff.end(), { return e.salary > 10000; }); 前两小段,是编程范式的简单介绍,C语言到C++的演化, 未完待续。。。。。 __EOF__ 作  者:fishers 出  处:https://www.cnblogs.com/fisherss/p/11503712.html 关于博主:热爱技术,评论和私信会在第一时间回复。 版权声明:署名 - 非商业性使用 - 禁止演绎,协议普通文本 | 协议法律文本。 声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!https://www.cnblogs.com/fisherss/p/11503712.html