轨道
/
Julia
Julia
/
练习
/
蒂斯伯里寻宝
蒂斯伯里寻宝

蒂斯伯里寻宝

学习练习

简介

Tuple在很多方面跟Vector(一维array)很像。不过,两者之间也有几处重要的不同。

  • 元组用圆括号( )书写,而不是方括号[ ];不过在含义明确的情况下,圆括号可以省略。
  • 元组不要求元素同类型:每个元素的类型都是独立存储的。
  • 元组是不可变的。

不可变性让编译器能做更多优化,也让元组的性能优于向量,所以在不需要可变性的情况下应该优先使用元组。

julia> t = (3, 5.2, "xyz")
(3, 5.2, "xyz")

# Non-homogeneous:
julia> dump(t)
Tuple{Int64, Float64, String}
  1: Int64 3
  2: Float64 5.2
  3: String "xyz"

julia> t[2]
5.2

# Immutable:
julia> t[2] = 7.3
ERROR: MethodError: no method matching setindex!(::Tuple{Int64, Float64, String})

还有一个Tuple()构造函数,可以把任何可迭代集合变成元组。很方便的是,它会把字符串视为由字符组成的可迭代集合。

julia> Tuple("Julia")
('J', 'u', 'l', 'i', 'a')

对任何元组,都可以通过下标访问元素,或者对它们进行循环。

任何纯数值元组都能用在sum()这类数学函数中,和数组完全一样。

解包

使用多重赋值可以把元组解包成一个个独立的元素。对于不需要的元素,用下划线_作为占位变量。

julia> tup = (1, "Julia", true)
(1, "Julia", true)

julia> num, name, _ = tup
(1, "Julia", true)

julia> uppercase(name), 2num
("JULIA", 2)

Python 程序员对这种多重赋值语法会非常熟悉。

具名元组

Tuple只包含值。NamedTuple则把每个值和一个名字配对。

这样就能用“点”表示法访问各个字段:

julia> nt = (a = 1, b = 7.3)
(a = 1, b = 7.3)

julia> dump(nt)
@NamedTuple{a::Int64, b::Float64}
  a: Int64 1
  b: Float64 7.3

julia> nt.b
7.3

名字在内部以Symbol的形式存储(一种不可变类型,前面带一个冒号:),所以还有另一种可用的语法:

julia> nt[:b]
7.3

要分别取出名字(也叫“键”)或值都很容易:

julia> nt
(a = 1, b = 7.3)

julia> keys(nt)
(:a, :b)

julia> values(nt)
(1, 7.3)

注意,遍历具名元组时只会产生_值_,_名字_会被忽略。

元组(无论是否具名)都是把结构化数据传入和传出函数的常见方式。在这种场景下,不可变性很有好处,它既能保证数据安全,也能让编译器进行更激进的优化。

说明

Azara 和 Rui 是队友,正在参加一场海盗主题的寻宝比赛。一个人拿着一份带地图坐标的宝藏清单,另一个人拿着一份带地图坐标的地点名称清单。他们还拿到了空白地图,上面的起点标着“你在这里”。

Azara 的清单 Rui 的清单
宝藏 坐标
Amethyst Octopus 1F
Angry Monkey Figurine 5B
Antique Glass Fishnet Float 3D
Brass Spyglass 4B
Carved Wooden Elephant 8C
Crystal Crab 6A
Glass Starfish 6D
Model Ship in Large Bottle 8A
Pirate Flag 7F
Robot Parrot 1C
Scrimshaw Whale's Tooth 2A
Silver Seahorse 4E
Vintage Pirate Hat 7E
地点名称 坐标 象限
Seaside Cottages ('1', 'C') Blue
Aqua Lagoon (Island of Mystery) ('1', 'F') Yellow
Deserted Docks ('2', 'A') Blue
Spiky Rocks ('3', 'D') Yellow
Abandoned Lighthouse ('4', 'B') Blue
Hidden Spring (Island of Mystery) ('4', 'E') Yellow
Stormy Breakwater ('5', 'B') Purple
Old Schooner ('6', 'A') Purple
Tangled Seaweed Patch ('6', 'D') Orange
Quiet Inlet (Island of Mystery) ('7', 'E') Orange
Windswept Hilltop (Island of Mystery) ('7', 'F') Orange
Harbor Managers Office ('8', 'A') Purple
Foggy Seacave ('8', 'C') Purple

不过事情有点乱:Azara 的坐标格式和排序似乎与 Rui 的不同,他们得不停地在两份清单之间来回对照,才能弄清楚哪个宝藏对应哪个地点。 作为初学 Julia 的程序员,他们来找你帮忙写一个小程序(其实是一组函数),好把寻宝信息整理得更清楚。

1. 提取坐标

实现get_cooordinate()函数,它接收 Azara 清单中的一个(treasure, coordinate)对,只返回提取出来的地图坐标。

julia> get_cooordinate( ("Scrimshaw Whale's Tooth", "2A") )
"2A"

2. 格式化坐标

实现convert_coordinate()函数,它接收一个 "2A" 格式的坐标,返回一个('2', 'A')格式的元组。

julia> convert_coordinate("2A")
('2', 'A')

3. 匹配坐标

实现compare_records()函数,它接收一个(treasure, coordinate)对和一个(location, coordinate, quadrant)记录,并比较双方的坐标。 如果坐标“匹配”,返回 true;如果不匹配,返回 false。 为准确比较,按需重新格式化坐标。

julia> compare_records(("Brass Spyglass", "4B"), ("Seaside Cottages", ('1', 'C'), "blue"))
false

julia> compare_records(("Model Ship in Large Bottle", "8A"), ("Harbor Managers Office", ('8', 'A'), "purple"))
true

4. 合并匹配的记录

实现create_record()函数,它接收 Azara 清单中的一个(treasure, coordinate)对和 Rui 清单中的一个(location, coordinate, quadrant)记录,如果坐标匹配,就返回(coordinate, location, quadrant, treasure)。

如果坐标_不_匹配,返回一个空元组。 为准确比较,按需重新格式化坐标。

julia> create_record(("Brass Spyglass", "4B"), ("Abandoned Lighthouse", ('4', 'B'), "Blue"))
("4B", "Abandoned Lighthouse", "Blue", "Brass Spyglass")

julia> create_record(("Brass Spyglass", "4B"), ("Seaside Cottages", ('1', 'C'), "Blue"))
()
通过 GitHub 编辑 链接将在新窗口或新标签页中打开
Julia Exercism

准备好开始 蒂斯伯里寻宝 了吗?

注册 Exercism,借助 35 个概念128 个练习 和真人导师指导,学习并掌握 Julia,全部免费。