Julia 元组
Julia 的元组与数组类似,都是有序的元素集合,不同之处在于元组的元素不能修改,元组是不可变类型。
另外元组使用小括号 (...),数组使用方括号 [...]。
元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可,数组中的很多函数也可以在元组中使用。
实例
julia> tupl = (5, 10, 15, 20, 25, 30) # 创建一个元组
(5, 10, 15, 20, 25, 30)
julia> tupl[3:end] # 输出第三个到最后一个元素的元组
(15, 20, 25, 30)
julia> tupl = ((1, 2), (3, 4)) # 创建嵌套元组
((1, 2), (3, 4))
julia> tupl[1] # 访问嵌套元组元素,输出第一个元组
(1, 2)
julia> tupl[1][2] # 输出第一个元组的第二个元素
2
(5, 10, 15, 20, 25, 30)
julia> tupl[3:end] # 输出第三个到最后一个元素的元组
(15, 20, 25, 30)
julia> tupl = ((1, 2), (3, 4)) # 创建嵌套元组
((1, 2), (3, 4))
julia> tupl[1] # 访问嵌套元组元素,输出第一个元组
(1, 2)
julia> tupl[1][2] # 输出第一个元组的第二个元素
2
注意:单元素元组必须多写一个逗号,写成
(1,)。因为(1)只是加括号的数字 1,不是元组。空元组写作()。
实例
julia> (1,)
(1,)
julia> (1) # 这只是数字 1
1
julia> typeof((1,))
Tuple{Int64}
(1,)
julia> (1) # 这只是数字 1
1
julia> typeof((1,))
Tuple{Int64}
元组是不可变的
元组的元素不能修改,如果我们尝试修改它就会报错:
实例
julia> tupl2 = (1, 2, 3, 4)
(1, 2, 3, 4)
julia> tupl2[2] = 0
ERROR: MethodError: no method matching setindex!(::NTuple{4, Int64}, ::Int64, ::Int64)
Stacktrace:
[...]
(1, 2, 3, 4)
julia> tupl2[2] = 0
ERROR: MethodError: no method matching setindex!(::NTuple{4, Int64}, ::Int64, ::Int64)
Stacktrace:
[...]
正因为不可变,元组可以安全地在多处共享、可以作为字典的键,编译器也能对它做更多优化。
解构赋值
元组最常见的用途之一是解构赋值:把一个元组(或任何可迭代对象)的值同时赋给多个变量:
实例
julia> x, y = (1, 2) # 解构赋值
(1, 2)
julia> x
1
julia> y
2
# 交换两个变量不需要临时变量
julia> x, y = y, x
(2, 1)
# 用 _ 忽略不关心的元素
julia> _, b = (10, 20)
(10, 20)
julia> b
20
(1, 2)
julia> x
1
julia> y
2
# 交换两个变量不需要临时变量
julia> x, y = y, x
(2, 1)
# 用 _ 忽略不关心的元素
julia> _, b = (10, 20)
(10, 20)
julia> b
20
具名元组(NamedTuple)
我们可以为元组的元素命名,从而可以像访问字段一样访问它。
1、键和值分开命名
实例
julia> names_shape = (:corner1, :corner2)
(:corner1, :corner2)
julia> values_shape = ((100, 100), (200, 200))
((100, 100), (200, 200))
julia> shape_item2 = NamedTuple{names_shape}(values_shape)
(corner1 = (100, 100), corner2 = (200, 200))
(:corner1, :corner2)
julia> values_shape = ((100, 100), (200, 200))
((100, 100), (200, 200))
julia> shape_item2 = NamedTuple{names_shape}(values_shape)
(corner1 = (100, 100), corner2 = (200, 200))
我们可以使用 . 点号来访问元素:
实例
julia> shape_item2.corner1
(100, 100)
julia> shape_item2.corner2
(200, 200)
(100, 100)
julia> shape_item2.corner2
(200, 200)
2、键和值同时在一个元组中
更常用的写法是直接在字面量中给每个元素命名:
实例
julia> shape_item = (corner1 = (1, 1), corner2 = (-1, -1), center = (0, 0))
(corner1 = (1, 1), corner2 = (-1, -1), center = (0, 0))
julia> shape_item.corner1
(1, 1)
julia> shape_item.center
(0, 0)
(corner1 = (1, 1), corner2 = (-1, -1), center = (0, 0))
julia> shape_item.corner1
(1, 1)
julia> shape_item.center
(0, 0)
我们还可以像使用普通元组一样解构所有值:
实例
julia> c1, c2, center = shape_item
(corner1 = (1, 1), corner2 = (-1, -1), center = (0, 0))
julia> c1
(1, 1)
(corner1 = (1, 1), corner2 = (-1, -1), center = (0, 0))
julia> c1
(1, 1)
具名元组的类型可以用 @NamedTuple 宏来书写,类似 struct 的声明语法:
实例
julia> typeof((a=1, b="hello"))
@NamedTuple{a::Int64, b::String}
# 用宏声明具名元组类型,还可以作为构造函数使用
julia> @NamedTuple{a::Float32, b::String}((1, ""))
(a = 1.0f0, b = "")
@NamedTuple{a::Int64, b::String}
# 用宏声明具名元组类型,还可以作为构造函数使用
julia> @NamedTuple{a::Float32, b::String}((1, ""))
(a = 1.0f0, b = "")
3、合并两个具名元组
我们可以使用 merge() 函数来合并两个具名元组,如果键重复,后面的值会覆盖前面的:
实例
julia> colors_shape = (top = "red", bottom = "green")
(top = "red", bottom = "green")
julia> shape_item = (corner1 = (1, 1), corner2 = (-1, -1), center = (0, 0))
(corner1 = (1, 1), corner2 = (-1, -1), center = (0, 0))
julia> merge(shape_item, colors_shape)
(corner1 = (1, 1), corner2 = (-1, -1), center = (0, 0), top = "red", bottom = "green")
(top = "red", bottom = "green")
julia> shape_item = (corner1 = (1, 1), corner2 = (-1, -1), center = (0, 0))
(corner1 = (1, 1), corner2 = (-1, -1), center = (0, 0))
julia> merge(shape_item, colors_shape)
(corner1 = (1, 1), corner2 = (-1, -1), center = (0, 0), top = "red", bottom = "green")
元组作为函数参数
具名元组可以作为关键字参数整体传给函数,用 元组名... 展开:
实例:test.jl 文件代码
# 创建函数
function testFunc(x, y, z; a=10, b=20, c=30)
println("x = $x, y = $y, z = $z; a = $a, b = $b, c = $c")
end
# 创建具名元组
options = (b = 200, c = 300)
# 执行函数,元组作为关键字参数传入
testFunc(1, 2, 3; options...)
function testFunc(x, y, z; a=10, b=20, c=30)
println("x = $x, y = $y, z = $z; a = $a, b = $b, c = $c")
end
# 创建具名元组
options = (b = 200, c = 300)
# 执行函数,元组作为关键字参数传入
testFunc(1, 2, 3; options...)
使用 julia 命令执行以上文件,输出结果为:
$ julia test.jl x = 1, y = 2, z = 3; a = 10, b = 200, c = 300
显式给出的关键字参数写在元组前面时会被元组覆盖,写在元组后面则会覆盖元组中的值:
实例
# 显式参数写在元组前,会被元组里的 b=200 覆盖
julia> testFunc(1, 2, 3; b = 1000000, options...)
x = 1, y = 2, z = 3; a = 10, b = 200, c = 300
# 显式参数写在元组后,覆盖元组里的 b
julia> testFunc(1, 2, 3; options..., b = 1000000)
x = 1, y = 2, z = 3; a = 10, b = 1000000, c = 300
julia> testFunc(1, 2, 3; b = 1000000, options...)
x = 1, y = 2, z = 3; a = 10, b = 200, c = 300
# 显式参数写在元组后,覆盖元组里的 b
julia> testFunc(1, 2, 3; options..., b = 1000000)
x = 1, y = 2, z = 3; a = 10, b = 1000000, c = 300
元组与数组的对比
| 特性 | 元组 Tuple | 数组 Vector |
|---|---|---|
| 语法 | (1, 2, 3) | [1, 2, 3] |
| 可变性 | 不可变 | 可变 |
| 元素类型 | 每个元素类型独立记录 | 所有元素同一类型 |
| 能否做字典键 | 可以 | 不可以 |
| 典型用途 | 函数多返回值、固定结构的数据 | 动态集合、数值计算 |
