6.4 类型操作
6.4.1 弱类型机制
在Julia中,类型与具体的值是密切相关的。值是占据内存的对象实体,是计算操作的真正目标,所以必然对应一个确切的类型,而且是可实例化的具体类型。即使是后续介绍的复合类型,其成员基础也是具体类型。
在类型与值之间,还有另外一个概念:变量。变量在Julia中并不像其他语言那样有着很重要的地位,而只是指代程序中某个元素的名称,仅是在定义时才绑定某个具体的值,只是对值的一次引用,以便后续对该值进行各种操作。而且这种微弱的绑定关系随时可以改变。例如:
julia> a = 10 10 julia> a = 3.2 3.2 julia> a = "this is not a number" "this is not a number"
其中,变量a先是用于指代整数10,再用于指代3.2这个浮点数值,最后又变成了字符串。
所以在Julia中,变量本身没有类型的概念,值(对象)才有。如果说一个变量是某类型,实际是指其引用的值属于该类型,这与C++或Java这些强类型语言是完全不同的。在这些强类型语言中,变量需要以声明的方式与某类型建立确切、唯一且恒定的关系,之后这个变量无法再变更类型,只能存取操作该类型的值。
在Julia这种弱类型机制中,变量无须先声明即可随时使用,而且不强制进行类型的限定。在语言内部,编译期的类型检查也不会太严格,一般直到运行时才确定操作目标对象的具体类型。这也是动态语言的基本特点。而且,变量的类型可以像上例那样随时变换(虽然这并不是Julia建议的做法)。
然而,过于松散的类型控制(例如允许数据类型随意变换或整型可与字符值相加),很容易导致开发的程序出现莫名其妙的错误。同时,如果类型延迟到运行期才确定,往往会带来很多效率损耗。所以Julia的编译器不会自动地对类型做隐式转换或提升,除非转换提升存在着明确的定义(语言内置的或用户定义的代码中);而且为了科学计算的高性能,Julia建议开发者在使用变量或值时,要尽可能地给出确切、详尽的类型描述,并能够维持类型的稳定性。
对于变量,如果希望其与值的绑定关系稳定,可以在定义时使用关键字const在定义时将其标识为常量。例如:
julia> const mye = 2.71828182845904523536; julia> const mypi = 3.14159265358979323846;
其中,mye和mypi均被定义为浮点数常量。
原则上,常量只能在声明时赋值一次。但正如上文所言,变量与值之间的赋值实际只是建立了两者的绑定关系,所以const在本质上影响的便是这种微弱的绑定关系。如果一定要对常量重新赋值,也是可以的,例如:
julia> mye = 3.21 WARNING: redefining constant mye 3.21 julia> mye 3.21
其中,强制性地修改了常量mye的值,但在返回新值前会报出“redefining”警告信息,不过仍会执行成功。但需要注意的是,如果试图将普通的同名变量再次定义为常量,会报异常(而不仅仅是个警告),即会执行失败:
julia> mypi = 3.14159 3.14159 julia> const mypi = 3.1415926 ERROR: cannot declare mypi constant; it already has a value