分享

编程思想|面向过程的结构化、面向对象的抽象化、泛型编程

 启云_9137 2020-07-06

程序总是因而解决问题而生,如何组合描述数据、算法的模块,就形成了各种编程思路的分类。

1 控制结构语句产生之前

20世纪60年代初,在提倡通过规则让读写程序更轻松的时代潮流中,结构化程序设计应运而生。时至今日,大家对if、while、for这样的结构控制语句早已习以为常。结构化程序设计的初衷正是通过导入这些语句使代码结构理解变得简单。

在没有if、while或for语句之前,通过goto语句和标签语句(实现一个语句入口)进行跳转实现分支和重复。

void use_while(int x){ printf('use_while/n');start_loop: if(!(x > 0)) goto end_loop; printf('%d/n', x) ; x--; goto start_loop;end_loop: return;}

goto语句仍然是高级语言的语法范畴,而跳转的原始实现可以追溯到机器语言和汇编语言的跳转指令。

汇编语言有无条件跳转指令jmp和有条件跳转指令族(以j打头)。对于比较操作,其底层是通过减法运算实现的,通过减法运算的结果改变标志寄存器,根据标志寄存器的状态控制跳转。

6:        int a=-3;00401268   mov         dword ptr [ebp-4],0FFFFFFFDh7:        if(a<0)0040126F   cmp         dword ptr [ebp-4],000401273   jge         main+2Dh (0040127d)8:            a=-a;00401275   mov         eax,dword ptr [ebp-4]00401278   neg         eax0040127A   mov         dword ptr [ebp-4],eax9:        a++;0040127D   mov         ecx,dword ptr [ebp-4]00401280   add         ecx,100401283   mov         dword ptr [ebp-4],ecx

2 结构化程序设计

相对于汇编语言的跳转指令和高级语言的goto诫勉,使用if、while或for等控制结构语句,可以让程序更易写、易读、更容易维护。

while语句,可以让反复执行的if语句更简洁:

void use_while2(int x){ printf('use_while/n'); while(x > 0){ printf('%d/n', x) ; x--; }}

for语句,可以让数值渐增的while语句更简洁:

	int i = 0;	int n = 10;	while(i < n){	    printf('%d\n', i);	    i++;	}		for (i = 0; i < n; i++)	{	    printf('%d\n', i);	}

3 面向对象

面向过程编程只是将函数封装为一个函数模块,数据和操作数据的函数处于分离的一个状态。

类是数据抽象、封装的最佳实践,如一个数组封装为类后,通过提供一个排序的接口sort(),其内部实现由类设计者完成,类使用者只需使用这个类接口即可,至于是使用冒泡排序还是快速排序,可由类设计者完成,即使内部实现不同的排序算法也不会影响到使用类接口的代码,这就是接口设计与使用分离的思想。

编程思想|面向过程的结构化、面向对象的抽象化、泛型编程

如complex、string类:

编程思想|面向过程的结构化、面向对象的抽象化、泛型编程

面向过程与面向对象:

编程思想|面向过程的结构化、面向对象的抽象化、泛型编程

面向过程与面向对象各自优、劣势:

编程思想|面向过程的结构化、面向对象的抽象化、泛型编程

当然,面向过程编程也称为面向函数编程,函数也有接口设计和使用的概念:

函数的内部观点:关心函数的定义,1 采用什么计算方法;2 采用什么实现结构;3 实际参数如何使用;4 怎样得到所需要的返回值;

函数的外部观点:关心函数的使用,1 实现了什么功能;2 名字是什么;3 要求几个参数,各参数的意义和作用;4 返回什么值;

4 泛型编程

如果数据和操作这些数据的函数二者之间是一种紧密结合的关系,那封装确实是一个最佳的选择,如数据结构的一些封装,如封装动态数组、链表、栈、队列,及这些数据结构的构造、增、删元素的操作。但是,考虑到一些如排序、查找等操作能够以较相似的接口来操作各类数据结构,便要将这些算法从各类数据结构的封装中独立起来,这便是泛型编程(Generic Programming,GP)的思想,C++的STL(Standard Template Library)便是GP的一个杰作。在STL中,在算法和作为数据结构的容器之间,以iterator_traits和iterator作为中间层,但实现了两者的独立性和结合性。

编程思想|面向过程的结构化、面向对象的抽象化、泛型编程
编程思想|面向过程的结构化、面向对象的抽象化、泛型编程
编程思想|面向过程的结构化、面向对象的抽象化、泛型编程

面向过程、面向对象、泛型:

编程思想|面向过程的结构化、面向对象的抽象化、泛型编程

5 命令式、声明式、动态式、函数式编程

不同的领域需要不同的语言,不同的语言有各自擅长的领域。选择一种语言和平台以满足解决全部问题的需要,这种方式已经一去不复返了。有时不同的钉子需要不同的锤子。

编程语言广义上可以分为命令式、声明式、动态式、函数式编程语言,体现的是提出问题、分析问题、描述问题和解决问题的思路的各个阶段中编译器所协助程序员完成的工作,这是另一种编程思想分类的体现。

5.1 命令式语言

传统的命令式语言描述了执行的方式,而不是执行的内容。命令式语言从一开始就设计为提供机器码的抽象级别。

命令式语言的语句主要是操作程序的状态。面向对象的语言就是经典的状态操作器,因为它们一直在创建和改变对象。VB、C、C++、C#都是命令式语言。

这些语言都非常擅长利用类型系统和对象描述现实世界中的情形。它们都非常严格-即编译器要进行许多安全检查。安全检查(或类型合理性)表示,不能轻易地把Cow类型变成Sheep类型,所以如果在方法的签名中声明需要一个Cow类型,编译器(和运行时)就要保证不会给方法传送Sheep类型。这些语言通常有奇异的重用机制-用多态性原则编写的代码很容易抽象出来。这样其他地方的代码,从同一模块到完全不同的项目,就可以利用已编写好的代码。

5.2 声明式语言

声明式语言描述了执行的内容,而不是执行方式(这与命令式语言相反,命令式语言描述了如何通过程序语句操作状态)。为人所熟知的声明式语言是HTML,它描述了页面的布局、需要的字体、文本和修饰,以及在哪里显示图像。另一个经典的声明式语言是SQL,它描述了要从关系数据库提取的内容。最近的一个声明式语言例子是XAML(eXtensible Application Markup Language),它引出了一大串基于XML的声明式语言。

声明式语言擅长描述和转换数据。多年来,我们从命令式语言中调用它们来检索和操作数据。

5.3 动态语言

动态语言包括具有“动态”特性的所有语言,如后期绑定和调用、REPL(Read Eval Print Loops)、鸭子类型化(不严格的类型化,即如果对象看起来像一只鸭子,行走起来也像一只鸭子,它就一定是一只鸭子)等。

动态语言一般会尽可能把编译的操作推迟到运行时执行。

像这样的方法编译有一些有趣副作用。如果类型不需要事先完全定义(因为类型系统非常灵活),就可以编写使用严格接口(例如,COM或者其他.NET程序集)代码,使这些代码在遇到该接口的失败时有很大的灵活性。

动态语言非常适合于快速建立原型,不必事先定义类型,就可以把注意力集中在解决问题的代码上,而不是集中在实现过程中的类型约束。REPL允许逐行编写原型代码,程序中的变化会立即反映出来,而不需要把时间浪费在编译-运行-调试循环上。

Python语言是一个经典的动态语言,Ruby也是。

5.4 函数式语言

'函数式编程'属于'结构化编程'的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。

函数式语言把计算看成数学函数,它们努力避免状态操作,而主要考虑函数的结果,以此作为解决问题的基础。如果你做过微积分,那么就会很熟悉函数式编程的理论。

因为函数式编程语言一般不操作状态,所以程序中生成的副作用就小很多。这意味着这类语言一直在执行并行算法。高度并行系统的圣杯是避免重叠“无意中”的状态操作。死锁、竟态条件以及被破坏的不变量都是没有同步状态操作代码的经典问题。通过线程、共享内存以及锁来并行编程和同步都非常困难,所以为什么不一起避免?因为函数式编程语言鼓励程序员编写无状态的算法,这样编译器就可以推断出代码的自行并行性。这意味着可以利用多核处理器的强大能力,而没有管理线程、锁和共享内存的巨大负担。

函数式程序是很简洁的,与命令式语言相比,函数式语言通常需要较少的代码就可以解决问题。代码较少一般意味着bug较少,要测试的区域也较少。

在语言中把每个类别的特性集混合起来是目前的一个趋势,这对程序员有好处,程序员喜欢使用的语言常会从每个类别中选择最佳特性。当前的趋势是应用程序开发人员使用命令式/动态语言,而函数式语言擅长解决特定领域的问题。

和指令式编程相比,函数式编程强调函数的计算比指令的执行重要。

和过程化编程相比,函数式编程里函数的计算可随时调用。

函数式编程中最古老的例子莫过于1958年被创造出来的LISP了,以及Haskell、Clean、Erlang和Miranda等。

-End-

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多