Julia 学习笔记——2. 数值系统、运算符与输出输出基础
现在关注 Julia 的一些独特语法和特性,会省略与 MATLAB 或 Python 相似的语法特性。
数值系统
整数
Julia 支持固定长度的整数类型,包括有符号和无符号的版本,例如
Int8,Int32,UInt32
等。对于十进制整数字面量,在64位系统中默认使用 Int64
类型整数。可以使用 typeof 查看字面量的类型
1
typeof(1) # Int64
类型也是可以作为参数进行运算的,例如使用 typemin 和
typemax 直接查看类型的最大最小值 1
2
3typemin(Int32) # -2147483648
typemin(Int64) # -9223372036854775808
typemax(Int64) # 9223372036854775807
注:
- Julia 支持使用
_作为数字的分隔符,可以提高可读性,例如10_000。 - 由于整数的位数是固定的,在运算中自然也存在溢出问题,与其它语言中的处理类似,不再赘述。
- Python 的整数是无限精度的,Julia 也提供了
BigInt类型以支持无限精度。
对于非十进制的整数字面量,Julia
采用了不同的处理:默认将其视作无类型整数,并自动选择合适的位数,例如
1
2typeof(0x11) # UInt8
typeof(0x1111111) # UInt32
浮点数
Julia 支持基本的浮点数类型,包括默认使用的双精度浮点数
Float64,单精度浮点数 Float32 和半精度浮点数
Float16。
有一点奇怪的是,在科学记数法的表示中,使用 e 会对应
Float64 的字面量,使用 f 则会对于
Float32 的字面量。 1
2typeof(1.1e-3) # Float64
typeof(1.1f-3) # Float32
可以使用类型名将整数转换为浮点数,例如 1
2
3Float64(1) # 1.0
typeof(Float64(1)) # Float64
有意思的是,Julia 给不同的浮点数类型分别设置了 nan 和
inf:
- Float64:
NaN和Inf - Float32:
NaN32和Inf32 - Float16:
NaN16和Inf16
关于这些特殊值的运算都符合 IEEE 标准,因此不同语言并没有什么区别。
Julia 的 eps
是一个函数,可以传入一个浮点数或者浮点数类型:
- 如果传入的是浮点数,那么返回的就是它附近的浮点数间隙;
- 如果传入的是浮点数类型,返回的是
1.0附近的浮点数间隙; - 缺省时相当于
eps(Float64)。
1 | eps(1000.0) # 1.1368683772161603e-13 |
Julia 提供了 zero 和 one
函数来创建指定类型的 0 和
1,可以传入类型或者一个数,在某些情况下可以避免不必要的类型转换。 例如
1
2zero(1.0) # 0.0
one(Int32) # 1
在整数和浮点数混合运算时,Julia 会自动转换为浮点数进行处理,并没有
MATLAB 那样的奇葩语法。 1
1 + 0.2 # 1.2
复数
Julia 支持复数,使用全局常量 im 表示虚数单位,例如
1
1 + 2im
有意思的是 1
2typeof(1+im) # Complex{Int64}
typeof(im) # Complex{Bool}
这表明复数并不是简单的一个类型,实际类型与复数的实部虚部所属的类型有关。
涉及到 im 的字面量需要特别注意运算优先级的差异,例如
1
23/4*im # 0.0 + 0.75im
3/4im # 0.0 - 0.75im
更建议的做法是使用 complex 函数来创建复数,例如
1
complex(1,2) # 1 + 2im
注意 Julia 严格区分字面量是否是复数类型,例如 -1 和
-1+0im 是完全不同的 1
2-1 == -1+0im # true
-1 === -1+0im # false
在使用 sqrt() 时的差异非常明显 1
2sqrt(-1) # ERROR
sqrt(-1+0im) # 0.0 + 1.0im
有理数
Julia 支持有理数类型,可以通过 // 运算符创建,例如
1
22//3
typeof(2//3) # Rational{Int64}
注:
- Julia 会自动对分数进行化简,并且保证分母非负。
- Julia 允许分母为0,例如
5//0,但是不允许分子分母同时为 0。 - Julia 不支持涉及浮点数的
//运算
字符和字符串
与 Python 不同,Julia 使用单引号 ' 表示字符,使用双引号
" 来表示字符串,两者是不同的类型,不可以混用。例如
1
2typeof('a') # Char
typeof("a") # String
字符的底层当然是基于 ASCII 存储的,因此也支持与数值的运算,例如
1
'a' + 1 # 'b'
Julia 支持多行字符串和 raw 标记的原始字符串(禁用
\ 转义,适合用于 Windows 路径),例如 1
2
3
4
5
6text = """
这是
多行字符串
"""
rawstr = raw"C:\Users\Alice"
Julia 有如下两种方式拼接字符串,注意使用 *
而不是 + 进行拼接! 1
2
3"Hello, " * "world!" # "Hello, world!"
join(["a", "b", "c"], ", ") # "a, b, c"
使用 ^ 表示字符串的重复 1
"abc"^4 # "abcabcabcabc"
与 Python 的 f-string 类似,在字符串中可以使用 $
插入变量或表达式,有歧义时可以加括号,例如 1
2
3
4
5name = "Alice"
greeting = "Hello, $(name)!" # "Hello, Alice!"
age = 25
info = "Age next year: $(age + 1)" # "Age next year: 26"
布尔值
提供布尔类型的 true 和 false。
在底层实现时,Julia 采用 8 位整数存储的 0 和 1 分别代表
true 和 false。相应的类型转换为
1
2Bool(1) # true
Bool(0) # false
需要注意到是,Julia 不会把非零整数全部转换为
true,下面的语句报错 1
Bool(2) # error
运算符
不含向量化运算的部分。
运算符和其它语言都差不多,只有几个值得注意的:
a*b:乘法a/b:除法a\b:反向除法,a\b等价于b/aa^b:幂运算a%b:取余==:相等,这和isequal函数不太一样!=: 不等===:严格比较二进制数据相等
注:
- 涉及到
NaN和Inf的等号和不等号判断可能违反直觉,例如NaN != NaN。 - 部分运算符对应的 Unicode(LaTeX)
字符也可以正常使用,例如不等号也支持
\leq ===、==和isequal的语义存在细微区别,尤其在某些边缘例子中,一般===最严格,isequal最弱。
与 Python 一样,Julia 支持链式比较,例如 1
1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5 # true
Julia 支持 += 等复合赋值运算符,例如 a += b
等价于 a = a + b,这个过程中可能改变 a
的类型,例如 1
2
3a=0; typeof(a) # Int64
a += 0.1; typeof(a) # Float64
! 是逻辑非,&&
是逻辑与,||
是逻辑或,并且特别注意&&和||都是短路运算,例如
1
2
3true && false # false
true || false # true
!true # false
~,& 和 |
是按位运算符,例如 1
2
3~0 # -1
123 & 234 # 106
1 | 2 | 4 # 7
容器
元组和具名元组
Julia 支持和 Python 几乎一样的元组,元组中的元素可以是不同类型,例如
1
2
3
4(1, 2)
1, 2
(1, "2", 3.0)
这里的括号在不存在歧义时可以省略,只含一个元素的元组和空元组有固定的写法,例如
1
2(1,)
()
元组中的元素可以使用索引访问(索引从1开始),例如 1
2
3a = (1,'a');
a[1] # 1
a[2] # 'a'
和 Python 一样,Julia
的元组赋值可以自动解包,因此可以用来同时定义多个变量,例如
1
2
3(x,y,z) = (1,2,3)
x,y,z = 1,2,3
与 Python 相比,Julia 的具名元组在语法上更加方便,例如
1
2
3a = (x=1, y=2);
a.x # 1
a.y # 2
这看起来非常适合用来打包不可变数据,比字典更加方便。
字典
Julia 的字典也和 Python 差不多,例如 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15dic1 = Dict("a" => 1, "b" => 2);
dic1["a"] # 1
dic1["b"] = 100;
length(dic1) # 2
keys(dic1)
# KeySet for a Dict{String, Int64} with 2 entries. Keys:
# "b"
# "a"
values(dic1)
# ValueIterator for a Dict{String, Int64} with 2 entries. Values:
# 100
# 1
字典的索引通常是字符串,但是实际上几乎可以是任何类型。
值得注意的是,在定义字典时使用的键值对并不是特殊语法,而是类似于 C++
的 std::pair 的数据结构 1
typeof("a"=>2) # Pair{String, Int64}
集合
Julia 的集合语法与 Python 明显不同:
- Julia 要求集合中的元素必须是同类型的,并且互不相等。(Python 只是要求不可变,用于确保唯一性)
- Python 支持使用花括号创建集合字面量,Julia 必须使用
Set()函数
例如 1
2
3
4
5
6
7
8
9
10
11set1 = Set([1, 2, 3, 2, 3])
# Set{Int64} with 3 elements:
# 2
# 3
# 1
set2 = Set(('a','b','c','a','b','c'))
# Set{Char} with 3 elements:
# 'a'
# 'c'
# 'b'
集合中的元素存储是无序的,可以使用 in
判断元素是否在集合中 1
2
3set1 = Set([1, 2, 3, 2, 3])
1 in set1 # true
支持集合间的基本运算,包括并集、交集、差集等。
数组
数组是科学计算中的核心容器,Julia 的这部分语法与 Python(Numpy) 和 MATLAB 均有所不同,相关的字面量语法非常混乱(比MATLAB还混乱)
Julia 提供数组来存储有序数据,通常用来存储同一类型的数据,当然也允许混合存储任意类型的数据,但是这会影响数组操作的效率。
TODO
输入输出基础
print/println
格式化输出
readline
parse与convert
补充
数值字面量系数
这是一个非常糟糕的,为了所谓的公式简洁优雅而提供的语法糖,实际上给代码可读性和语法解析都带来了额外的问题。
Julia 允许变量直接跟在一个数值字面量后,暗指乘法,例如
1
2
3x = 3
2x^2 + 4x + 5 # 21
6.0x^3-x # 46.0
数值字面量系数的优先级跟一元运算符相同,比如取相反数,例如:
2^3x会被解析成2^(3x)2x^3会被解析成2*(x^3)
括号表达式在某些情况下可以被用作变量的系数,暗指表达式与变量相乘,例如
1 | x = 3 |
但是这玩意其实很鸡肋,因为下面的表达式都是语法错误:
1 | x = 3 |
而且这里还可能和其它语法产生冲突,例如函数调用,十六进制的前缀0x,用于浮点数表示的f和e,以及虚数单位im等。
总的来说,字面量系数这种奇葩语法糖还是了解即可,实践中不要使用。
