第五课 语言小谈(2)
编程
5.2数据类型——规则与变通安全
操纵于规矩之中,神明于规矩以外 ——《俞震·古今医案按》less
关键词: 数据类型,静态类型,动态类型,Duck类型,强类型,弱类型,类型安全编程语言
摘要: 关于数据类型的讨论ide
!预览函数
· Duck类型的哲学是:名义不重要,重要的是能力优化
· 将一个会叫会游的家伙放进池塘看起来不算坏主意,但若是一艘轮船趁机也轰隆隆地开了进来,事情恐怕就不那么美妙了spa
· 静态类型检查相似“疑罪从有”的有罪推定制,动态类型检查相似“疑罪从无”的无罪推定制.net
· 尽量守规则,必要时求变通orm
· 规则如裤带,过于宽松和过于束缚都很差
?提问
· 动态语言与动态类型语言是一回事吗?
· 数据类型有哪两个要素?其意义何在?
· 什么是动态类型和静态类型?它们的区别是什么?各有什么优缺点?
· 什么是鸭子类型(duck typing)?它有什么优缺点?
· 什么是强类型与弱类型?什么是类型安全的?
:讲解
待教室平静下来,冒号再度开腔:“在谈论动态语言以前,最好先澄清一下它与动态类型语言之间的区别。”
叹号讶然道:“它们不是一回事吗?一直觉得动态语言是动态类型语言的简称呢。”
“有亲戚之名,却无血缘之亲。名称上类似,加之动态语言绝大多数确是动态类型语言,形成混淆实属在所不免,但两者之间并没有必然联系——动态语言不必定是动态类型语言[1],动态类型语言也不必定是动态语言[2]。”冒号飞跑的舌头几乎绊蒜,同时把众人的脑子搅成了一锅粥。
见势不妙,冒号改用迂回战术:“咱们不妨再谈开些,你们对数据类型是如何理解的?”
逗号随口道:“数据类型不就是数据的种类吗?”
众人暗笑:说了跟没说差很少。
冒号说道:“数据类型包含两个要素:一个是容许取值的集合,一个是容许参与的运算。例如int类型在Java中既定义了介于− 231 和231 − 1之间的整数集合,也定义了该集合上的整数所能进行的运算。如今的问题是:数据类型的意义何在?”
句号回答:“限定一个变量的数据类型,就意味着限制了该变量的取值范围和所参与的运算,这从必定程度上保证了代码的安全性。”
冒号追问:“还有吗?”
句号略做思考后说:“用户自定义的数据类型,如C中的结构和Java中的类或接口,赋予数据以逻辑内涵,提升了代码的抽象性。”
“精辟!”冒号赞道,“数据类型既有针对机器的物理意义,又有针对人的逻辑意义。前者用于进行底层的内存分配和数值运算等,后者用于表达高层的逻辑概念。既然类型如此重要,类型检查就必不可少了[3]。所谓动态类型语言(dynamic typing language),正是指类型检查发生在运行期间(run-time)的语言。”
“那静态类型语言(static typing language)天然是类型检查发生在编译期间(compile-time)的语言咯。”引号接话道。
冒号回应:“通常的说法是这样,但我更愿意将‘编译期间’四个字改成‘运行以前’,不然容易让人误解为静态类型语言必定是编译型语言(compiled language)。”
问号问道:“是否能够这么说:静态类型语言须要变量声明,而动态类型语言则不须要?”
“这话只对了一半。”冒号评论,“动态类型语言当然不须要显式的变量声明(explicit declaration),一些静态类型语言有时也不须要。典型的如ML、Haskell之类的函数式语言,编译器能够经过上下文来进行类型推断(type inference)。”
“如何进行类型推断?”问号有点丈二和尚摸不着头脑。
冒号打了个比方:“假设‘+’号只限于同类型的数据运算,那么从表达式a + 1中能够推出a是整型变量,从b + 1.0中推出b是浮点型变量,从c + “1”中推出c是字符串型变量。这些变量没必要事先声明,但一旦类型被推断肯定后,便再也不更改。因为这些推断都是在程序运行以前进行的,所以仍属于静态类型。它既有动态类型的简洁性,又不失声明式静态类型的安全性,可谓裁长补短啊。”
叹号有些羡慕地说:“仍是动态类型语言好,不只没必要声明变量,并且一个变量在不一样地方还能够表明不一样类型,多省事多方便啊!”
冒号微微颔首:“虽然这种机制也有为人诟病之处,但不能否认,动态类型语言的确有它的优点:简明、快捷、灵活,而且自然具备泛型(generic)特征。值得一提的是,动态类型有一种被称做鸭子类型(duck typing)的形式。”
逗号感到有趣:“鸭子类型?很滑稽的名字。”
“这种类型通俗的说法是:若是一个对象既会走鸭步又会呷呷叫,何妨将其视做鸭子呢?”冒号说着投影出一段Ruby代码——
class Duck #会叫会游的鸭
def shout
puts '呷呷呷'
end
def swim
puts '鸭泳'
end
end
class Frog #会叫会游的蛙
def shout
puts '呱呱呱'
end
def swim
puts '蛙泳'
end
end
def shoutAndSwim(duck) #让一只会叫会游的家伙边叫边游
duck.shout
duck.swim
end
shoutAndSwim(Duck.new) #让一只鸭边叫边游
shoutAndSwim(Frog.new) #让一只蛙边叫边游
冒号继续讲解:“在Smalltalk、Python和Ruby等动态类型的OOP语言中,只要一个类型具备shout和swim的方法,它就能够为shoutAndSwim所接受。这在C++、Java、C#等静态类型语言中是不可能的[4],除非鸭和蛙在同一继承树上,或者两者均显式实现了一个包含shout和swim的公用接口。”
句号敏锐地指出:“C++是静态类型语言,但它的模板也可实现相似功能,并不须要引入继承关系。”
“说得很对!但请接着看下去。”冒号又放出一段投影——
class Cock #会叫不会游的鸡
def shout
puts '喔喔喔'
end
end
class Fish #会游不会叫的鱼
def swim
puts '自由泳'
end
end
def shoutOrSwim(duck, flag) #让一只会叫或会游的家伙叫或游
flag ? duck.shout : duck.swim
end
shoutOrSwim(Cock.new, true) #让一只鸡叫
shoutOrSwim(Fish.new, false) #让一只鱼游
“这里鸡没有swim的方法,鱼没有shout的方法。若采用C++的模板,shoutOrSwim是没法经过编译的。但在支持Duck 类型的语言中,只要在运行期间不让鸡swim、让鱼shout——除非你突发奇想——一切平安无事。”冒号做了个OK的手势。
“动态类型语言真是越看越可爱。”叹号简直垂涎欲滴了。
“Duck类型的哲学是:名义不重要,重要的是能力,很有些实用主义的味道。这种非继承性多态为软件重用开启了新的窗口,同时也埋下了一些陷阱。因为Duck类型的接口组合是隐性的,其使用者须要比普通interface更当心以免误用;其维护者也须要更当心以免破坏客户代码;另外它也可能形成滥用——将一个会叫会游的家伙放进池塘看起来不算坏主意,但若是一艘轮船趁机也轰隆隆地开了进来,事情恐怕就不那么美妙了。”
众皆莞尔。
“再来看看静态类型语言的好处:因为在运行以前进行了类型检查,一方面代码的可靠性加强,符合发现错误要尽早的原则;另外一方面编译器有可能藉此优化机器代码以提升运行效率,同时相比前者节省了运行期的耗费在类型检查上的时间和空间。此外,变量类型的声明彰显了编程者的意图,有辅助文档的功效。”冒号有条有理地解释着,“两种类型的体制能够用两种法律原则来类比:静态类型检查相似‘疑罪从有’的有罪推定制——在被证实合法以前是非法的,动态类型检查相似‘疑罪从无’的无罪推定制——在被证实非法以前是合法的。至于如何取舍,套用一句话:‘Static Typing Where Possible, Dynamic Typing When Needed’。不妨理解为:尽量守规则,必要时求变通。”
句号俏皮地说:“规则如裤带,过于宽松和过于束缚都很差。”
问号提出新问题:“动态类型语言与弱类型语言有何不一样?”
冒号喟言:“它们也经常被混为一谈,但类型的动静与强弱彻底是正交的两个概念。静态类型语言中,有强类型的Java,也有弱类型的C;动态类型语言中,有强类型的Smalltalk,也有弱类型的JavaScript。前者以类型的绑定(binding)时间来划分,后者以类型的约束强度来划分。一般弱类型语言(weakly-typing language)容许一种类型的值隐性转化为另外一种类型[5]。举个例子,1+"2"在VB中等于3——第二个字符串转化为整数;在JavaScript中等于"12"——第一个整数转化为字符串;在C中则等于一个不定的整数值——第二个字符串做为地址来运算。这样彷佛颇有趣很方便,但程序容易藏污纳垢,滋生臭虫(bug)。与此相对地,强类型语言(strongly-typed language)着意贯彻类型控制,为保障数据的完整性和代码的安全有效性,通常不容许隐性类型转换[6]。若是必定须要类型转换,必须是显性转换,通常经过咱们熟知的铸型(cast)来完成。”
引号想起:“好像还有一种所谓的类型安全语言?”
逗号牢牢抱着头,仿佛惧怕裂开。
“类型按安全性来划分,可分为类型安全语言(type-safe language)和类型不安全语言(type-unsafe language)。类型检查的目的就是为了不类型错误(type error)[7],即杜绝因类型问题而产生的错误或不良代码。若是一个类型系统能彻底作到这一点,它就被称为类型安全的。虽然尚存争议,但通常认为强类型语言对类型控制更严格,于是是类型安全的,弱类型语言是类型不安全的。类型安全当然对保障程序的合理性和可靠性十分重要,但若过于严苛,程序也就失去了活力,正所谓‘水至清则无鱼’啊。” 冒号有条不紊地解说着,“至此,咱们已论及数据类型的三种划分方式。须要说明的是,这些划分并不是泾渭分明的[8],更多的是定性而非定量的描述,甚至没有公认统一的定义。但了解它们,对咱们理解编程语言和编程原则是大有裨益的。”
,插语
[1] Scala是动态语言,倒是静态类型的。
[2] Visual Basic(不包括VB.NET) 支持动态类型,倒是静态语言。
[3] 极少数语言没有类型检查(untyped或typeless),如大多数汇编语言、Forth语言等。
[4] C#4.0将支持duck typing。
[5] 隐式转换也称为强制转换(coercion)。有人将显式转换的铸型(cast)译为强制转换,并不许确。
[6] 但许多强类型语言对于宽转换(widening conversion)仍是容许隐性的,如必要时int可自动转换为float。
[7] 典型的类型错误是:一个函数原本期待的参数类型是A,实际传入的变量a却不是A类型。
[8] 好比,静态类型的OOP语言如C++、Java支持downcasting,能在运行期间进一步细化数据类型,从某种意义上也具备动态类型的特征。
。总结
“”参考
[1] Wikipedia.Type system.http://en.wikipedia.org/wiki/Type_system
[2] Erik Meijer,Peter Drayton.Static Typing Where Possible, Dynamic Typing When Needed.http://research.microsoft.com/~emeijer/Papers/RDL04Meijer.pdf
[3] Ravi Sethi.Programming Languages: Concepts & Constructs(英文版第2版).北京:机械工业出版社,2002.136-143
来自 “ ITPUB博客 ” ,连接:http://blog.itpub.net/14674535/viewspace-526592/,如需转载,请注明出处,不然将追究法律责任。
转载于:http://blog.itpub.net/14674535/viewspace-526592/