轨道
/
Go
Go
/
练习
/
追求速度
追求速度

追求速度

学习练习

简介

在 Go 中,struct 是一系列具名元素组成的序列,这些元素称为_字段_,每个字段都有名字和类型。 字段名在同一个结构体内必须唯一。 结构体可以类比面向对象编程范式中的_类_。

定义结构体

使用 type 和 struct 关键字可以创建新的结构体,并显式定义各字段的名字和类型。 例如,下面定义了一个Shape结构体,用来保存形状的名字和大小:

type Shape struct {
    name string
    size int
}

结构体中的字段名遵循 Go 的约定:以小写字母开头的字段只对同一个包中的代码可见,而以大写字母开头的字段对其他包也可见。

创建结构体实例

定义好struct之后,你需要创建新的实例。创建时用字段名来指定各个字段的值,顺序任意:

s := Shape {
    name: "Square",
    size: 25,
}

要读取或修改实例的字段,使用 . 记法:

// Update the name and the size
s.name = "Circle"
s.size = 35
fmt.Printf("name: %s size: %d\n", s.name, s.size)
// Output: name: Circle size: 35

没有赋初值的字段会取零值。 例如:

s := Shape{name: "Circle"}
fmt.Printf("name: %s size: %d\n", s.name, s.size)
// Output: name: Circle size: 0

只要按_顺序_定义字段,创建struct实例时就可以不使用字段名:

s := Shape {
	"Oval",
	20,
}

不过,这种写法被视为_脆弱代码_,因为一旦新增字段就可能失效,尤其是当新字段的类型不同时。 在下面的例子中,我们给Shape额外添加了一个字段:

type Shape struct {
	name        string
	description string // new field 'description' added
	size        int
}

s := Shape{
    "Circle",
    20,
}
// Since the second field of the struct is now a string and not an int,
// the compiler will throw an error when compiling the program:
// Output: cannot use 20 (type untyped int) as type string in field value
// Output: too few values in Shape{...}

“New”函数

有时,有一些能帮我们创建结构体实例的函数会很方便。 按照惯例,这类函数通常命名为New,或者名字以New开头;不过它们只是普通函数,你可以随便取名。 它们可能会让你想起其他语言中的构造函数,但在 Go 里,它们只是普通函数。

在下面的例子中,我们用这样一个New函数创建Shape的新实例,并自动为形状的size设置默认值:

func NewShape(name string) Shape {
	return Shape{
		name: name,
		size: 100, //default-value for size is 100
	}
}

NewShape("Triangle")
// => Shape { name: "Triangle", size: 100 }

使用New函数有以下几个好处:

  • 校验传入的值
  • 处理默认值
  • 由于New函数通常与被初始化的结构体声明在同一个包中,因此它们甚至能初始化结构体的私有字段

说明

在这个练习中,你将组织各种类型遥控车之间的比赛。 每辆车都有自己的速度和电池消耗特性。

赛车的初始电池电量都是满的(100%)。 每次你使用遥控器驱动赛车时,它都会前进相当于该车速度的距离(以米为单位),并根据电池消耗减少剩余的电池百分比。

如果一辆车的电池电量低于它的电池消耗百分比,你就不能再驱动这辆车了。

每条赛道都有自己的距离。 检验赛车的方式,是看它们能否在电池耗尽前跑完整条赛道。

1. 创建一辆遥控车

定义一个Car结构体,包含以下int类型的字段:

  • battery
  • batteryDrain
  • speed
  • distance

通过定义一个函数NewCar来创建遥控车,它接收两个形参:赛车的速度(以米为单位)和电池消耗百分比(都是int类型),并返回一个Car实例:

speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
// => Car{speed: 5, batteryDrain: 2, battery:100, distance: 0}

2. 创建一条赛道

再定义一个结构体类型Track,它有一个整数类型的字段distance。 通过定义一个函数NewTrack来创建赛道,它唯一的形参是赛道的距离(以米为单位,类型为int):

distance := 800
track := NewTrack(distance)
// => Track{distance: 800}

3. 驱动赛车

实现Drive函数,根据赛车的速度更新已行驶的米数,并按电池消耗减少电量。 如果剩余电量不足以再驱动一次,赛车就不会移动:

speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
car = Drive(car)
// => Car{speed: 5, batteryDrain: 2, battery: 98, distance: 5}

4. 检查遥控车能否完成比赛

要完成一场比赛,赛车必须能跑完比赛的距离。 这意味着在冲过终点线之前,它的电池不能耗尽。 实现CanFinish函数,它接收一个Car实例和一个Track实例作为形参,如果赛车能完成比赛就返回true;否则返回false。

假设你现在正位于比赛的起跑线上,并且已经为比赛发动了赛车引擎。 注意,比赛开始时赛车的电池未必是满的:

speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)

distance := 100
track := NewTrack(distance)

CanFinish(car, track)
// => true
通过 GitHub 编辑 链接将在新窗口或新标签页中打开
Go Exercism

准备好开始 追求速度 了吗?

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