轨道
/
Go
Go
/
练习
/
分拣室
分拣室

分拣室

学习练习

简介

类型转换

Go 要求在不同类型之间进行显式转换。 类型之间的转换(也叫类型强制转换)通过一个以目标类型名为名称的函数来完成。 例如,要把 int 转换成 float64,需要这样做:

var x int = 42 // x has type int
f := float64(x) // f has type float64 (ie. 42.0)

基本类型与字符串之间的转换

strconv 包可以在基本类型(如 int)和 string 之间进行转换。

import "strconv"

var intString string = "42"
var i, err = strconv.Atoi(intString)

var number int = 12
var s string = strconv.Itoa(number)

类型断言

Go 中的接口可能会让底层类型变得不明确。 类型断言让我们可以用这样的语法提取接口值底层的具体值:interfaceVariable.(concreteType)。

例如:

var input any = 12
number := input.(int)

注意:如果接口变量持有的值不是该具体类型,就会引发 panic。

利用类型断言的两个返回值,我们可以判断接口值是否持有某个具体类型:一个是底层的值,另一个是表示断言是否成功的布尔值。 例如:

str, ok := input.(string) // no panic if input is not a string

如果 input 持有的是 string,那么 str 就是底层的值,ok 为 true。 如果 input 持有的不是 string,那么 str 就是 string 类型的零值(即 "",空字符串),ok 为 false。 无论哪种情况都不会发生 panic。

类型分支

类型分支可以依次执行多个类型断言。 它的语法和类型断言相同(interfaceVariable.(concreteType)),只是把 concreteType 换成了关键字 type。 下面是一个例子:

var i any = 12 // try: 12.3, true, int64(12), []int{}, map[string]int{}

switch v := i.(type) {
case int:
    fmt.Printf("the integer %d\n", v)
case string:
    fmt.Printf("the string %s\n", v)
default:
    fmt.Printf("type, %T, not handled explicitly: %#v\n", v, v)
}

说明

珍在一家大型工厂的分拣室工作。 分拣室需要处理送进来的一切东西,给它们贴上标签分类。 珍负责那些被预先归类为数字的东西,她需要一个程序来帮她分拣。

大多数基本值都应该得到简单直接的标签。 对于数字,她想要"This is the number 2.0"这样的字符串(如果这个数字是 2)。 无论是整数还是浮点数,珍都想要同样的输出。

有几个Box接口,需要拆开它们才能取出里面的内容。 对于NumberBox,她想要"This is a box containing the number 3.0"这样的字符串(如果Number()方法返回 3)。 对于FancyNumberBox,她想要"This is a fancy box containing the number 4.0"这样的字符串,但只有当它的类型是FancyNumber时才可以。

任何意料之外的东西都应该返回"Return to sender",这样珍就能把它们退回到原来的地方。

1. 描述简单的数字

珍希望数字返回像"This is the number 2.0"这样的字符串(小数点后保留一位):

fmt.Println(DescribeNumber(-12.345))
// Output: This is the number -12.3

2. 描述数字盒子

珍希望数字盒子返回像"This is a box containing the number 2.0"这样的字符串(同样,小数点后保留一位):

fmt.Println(DescribeNumberBox(numberBoxContaining{12}))
// Output: This is a box containing the number 12.0

3. 实现一个从花式数字盒子中提取数字的方法

珍需要一个辅助函数,从FancyNumberBox中提取数字。 如果FancyNumberBox是FancyNumber,就提取它的值,并把string转换成int。 其他任何类型的FancyNumberBox都应该返回 0。

fmt.Println(ExtractFancyNumber(FancyNumber{"10"}))
// Output: 10
fmt.Println(ExtractFancyNumber(AnotherFancyNumber{"4"}))
// Output: 0

4. 描述花式数字盒子

如果FancyNumberBox是FancyNumber,珍想要"This is a fancy box containing the number 4.0"这样的字符串。 其他任何类型的FancyNumberBox都应该返回"This is a fancy box containing the number 0.0"。

fmt.Println(DescribeFancyNumberBox(FancyNumber{"10"}))
// Output: This is a fancy box containing the number 10.0
fmt.Println(DescribeFancyNumberBox(AnotherFancyNumber{"4"}))
// Output: This is a fancy box containing the number 0.0

注意:我们应该使用ExtractFancyNumber函数!

5. 实现会用到以上所有的DescribeAnything

这是珍需要的主函数,它接受任意输入。 DescribeAnything应该根据传入值的类型,把工作委托给其他函数。 具体来说:

  • int和float64都应该委托给DescribeNumber
  • NumberBox应该委托给DescribeNumberBox
  • FancyNumberBox应该委托给DescribeFancyNumberBox
  • 其他任何情况都应该返回"Return to sender"
fmt.Println(DescribeAnything(numberBoxContaining{12.345}))
// Output: This is a box containing the number 12.3
fmt.Println(DescribeAnything("some string"))
// Output: Return to sender
通过 GitHub 编辑 链接将在新窗口或新标签页中打开
Go Exercism

准备好开始 分拣室 了吗?

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