编程语言的动态性
类型系统层面上的动态性
根据类型检查是在编译期还是在运行期进行的,我们可以把计算机语言分为两类:
- 静态类型语言(全部或者几乎全部的类型检查是在编译期进行的)。因为编译期做了类型检查,运行期不用再检查类型,性能更高。像 C、Java 和 Go 语言,在编译时就对类型做很多处理,包括检查类型是否匹配,以及进行缺省的类型转换
- 动态类型语言(类型的检查是在运行期进行的)。
泛型编程
来自陈皓《左耳听风》笔记
编程语言的本质是帮助程序猿屏蔽底层机器代码,使得我们可以更为关注业务逻辑代码。
programming paradigm,范即模范之意,是一类典型的编程风格,不同的风格解决的都是同一个问题:如何写出更为通用、更具可重用性的代码或模块。
作者提供了一个视角,从“程序=算法 + 数据结构”出发,从语言对泛型的支持来看待语言的演化:泛型编程的支持。逻辑重用 ==> 算法复用性高 ==> 数据结构标准化 ==> 类型泛型。简单说就是让数据结构 迁就算法。
-
对于程序=算法+数据结构,C语言有以下问题:一个通用的算法,需要对所处理的数据的数据类型进行适配。
-
C语言的伟大之处在于 程序猿可以在高级语言的特性之上还能简单的做任何底层上的微观控制。但在编程这个世界中,更多的编程工作是解决业务上的问题,而不是计算机的问题。
-
C++的作者有一本书《C++语言的设计和演化》这本书系统介绍了C++诞生的背景和初衷: 早先很多是对C的强化和净化 ==> 用引用解决指针问题;用class来解决对象的创建、赋值、销毁等问题(C指针干这些都要手动)。泛型编程是C++的重点
-
理想情况下,算法应是和数据结构以及类型无关的,各种特殊的数据类型理应做好自己分内的工作。算法只关心一个标准的实现。而对于泛型的抽象,我们需要回答的问题是:如果我们的数据类型符合通用算法,那么对数据类型的最小需求是什么?
1)减少自定义数据类型 与 内建数据类型的差异。比如漏出构建、析构、克隆等函数 的hook交由语言自动执行,类似int自动帮你初始化为0.
2)减少自定义数据类型之间的差异。List、Set 抽象为Iterator,而调用Iterator.next 可以按照实际的数据类型反应。从泛型的角度理解Iterator 的价值
-
使用动态类型语言写sum(x,y)貌似没有泛型的问题,但在你要明确类型时又很难受,比如将一个不知道什么类型的x转为一个数字。
一个良好的泛型编程需要解决 几个问题
- 算法的泛型,将sum 和max、min等都抽象为 reduce(把数组聚合为一个值),也就是将for 循环之内的 逻辑/算法 作为参数传入。
- 类型的泛型,比如sum int 改为sum T。
- 数据结构的泛型,比如找到一种公共的方式来描述List、Set、Map 等 ==> Iterator
泛型编程于1985年在论文 generic programming 中被这样定义:Generic programming centers around the idea of abstracting from concrete, efficient algorithms to obtain generic algorithms that can be combined with different data representations to produce a wide variety of useful software. 屏蔽掉数据和操作数据的细节,让算法更为通用,让编程者更多的关注算法的结构,而不是在算法那中处理不同的数据类型。
编程语言层面上的动态性
语言的动态性要点如下:
-
不同的语言具有不同程度的动态特性,现代大多数语言都是介于二者之间的折中。下面是两个极端:
1)Fortran语言不支持堆栈,所有的变量和子程序都是在编译时分配好内存的,不能进行动态内存分配,因而不能进行函数递归调用。
2)Perl、Python和Ruby语言可以在运行时修改类的结构或定义,变量的类型可以按需改变,Lisp语言甚至可以在运行时动态地改变自身的代码
-
动态语言有多种定义,本文采用:动态语言是指能够在运行时改变程序结构和变量类型的语言
-
程序中定义的操作一般需要特定类型的参数作为操作的输入,操作只有在接收到类型正确的参数时才能正确无误的执行(包括操作内存)。所以无论哪种语言,都逃避不了一个特定的类型系统
-
动态类型语言在每个数据对象中保存一个类型标签表明该数据对象的类型,在运行时进行动态类型检查。比如在表达式C=A+B中,A和B的类型在程序运行时确定,也可以在运行时改变,所以每次执行 + 操作时都要根据类型标签对A和B的类型进行检查
-
动态类型检查的主要优点在于程序设计的灵活性,不需要声明语句,一个变量名绑定的数据对象的类型可以在程序执行时按需改变,使程序员从数据类型摆脱出来。运行时进行的类型检查也存在几点重大不足:
1)程序难以调试。只在程序运行到某一条操作时才对其进行类型检查,软件测试时是不能遍历程序中所有的执行路径,这样没有被执行的路径仍有可能存在bugs
2)保存大量的类型信息。运行时需要相当大的额外存储空间
3)执行效率低,动态类型检查要靠软件模拟实现,主要是在运行时完成的
Python中的对象和动态性
当我们谈到python时常常会说python中一切都是对象,字符串是对象,整数型是对象,标准库中的对象当然也是对象,class本身也是对象,类型(type)也是对象。
但是初学的时候会错误地认为,既然大家都是对象,那么应该都是一样的“众生平等”啦。非也非也,尽管都是对象,每个对象支持的方法和属性还是很不一样的啦。
就拿int和自定义的类型来说吧。
int型是python语言的原始类型,上图中所见的就是它所支持的所有操作,如加减乘除,移位,取反等等。
除了面向对象这个特点,Python的另一个特点是它是一种动态语言。好吧,亲,我们来给int对象加上一个自定义属性,何如?
肿么啦,看起来不灵啊。好吧,我们自己建个类型来试试看。
自定义类型Object是个空类型,是一穷二白的状态。没关系,我们来整整。
现在obj已经加上了一个叫做name的属性,再来加上方法吧。
我选择了给类Object加上方法,当然如果你喜欢也可以只给obj实例添加一个方法。方法echo没有做什么石破天惊的事情,
仅仅输出了自己的名字。
一个Object显得有些孤单,我们给他制造个兄弟吧,然后让他兄弟也echo一把。
呀,肿么回事?哦,这个兄弟没有name属性啊,因为之前我们仅仅给他哥哥取了名字。来,接着整。
给类型Object加上name属性,然后看看obj2和obj。
结果如上,一切尽在掌握之中,嘿嘿。通过上面的例子,python的动态性有一定体现.
If you love him, teach him C++, for it's heaven; If you hate him, teach him C++, for it's hell