题目一
1、为 float64 定义一个别名类型 Celsius,并给它定义 String(),它输出一个十进制数和 °C 表示的温度值。
package main
import "fmt"
// 为 float64 定义一个别名类型 Celsius,并给它定义 String(),它输出一个十进制数和 °C 表示的温度值。
type Celsius float64
func (c Celsius) String() string {
return fmt.Sprintf("%g °C", c)
}
func main() {
c := Celsius(23.5)
fmt.Println(c.String())
}
解释:
在上面的代码中,我使用 type 关键字定义了一个新的类型 Celsius,它是 float64 的别名类型。然后,在 Celsius 类型上定义了 String() 方法,该方法返回一个格式化的字符串,表示温度值和单位 °C。
在 main() 函数中,我创建了一个 Celsius 类型的变量 c,并打印了它的 String() 方法的输出结果。 输出结果为 23.5°C。
题目二
2、为 int 定义一个别名类型 Day,定义一个字符串数组它包含一周七天的名字,为类型 Day 定义 String() 方法,它输出星期几的名字。使用 iota 定义一个枚举常量用于表示一周的中每天(MO、TU…)。
package main
import "fmt"
// 为 int 定义一个别名类型 Day,定义一个字符串数组它包含一周七天的名字,
// 为类型 Day 定义 String() 方法,它输出星期几的名字。
// 使用 iota 定义一个枚举常量用于表示一周的中每天(MO、TU...)。
type Day int
const (
MO Day = iota
TU
WE
TH
FR
SA
SU
)
var dayNames = [...]string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}
func (d Day) String() string {
if d < MO || d > SU {
return "Wrong, not the week"
}
return dayNames[d]
}
func main() {
fmt.Println(MO.String())
fmt.Println(TU.String())
fmt.Println(WE.String())
fmt.Println(TH.String())
fmt.Println(FR.String())
fmt.Println(SA.String())
fmt.Println(SU.String())
}
解释:
在上面的代码中,我使用 type 关键字定义了一个新的类型 Day,它是 int 的别名类型。然后,我使用 const 和 iota 定义了一周中每天的枚举常量,从 MO 开始,逐个加1。
我还定义了一个包级变量 dayNames,它是一个字符串数组,包含了一周七天的名字。
最后,我在 Day 类型上定义了 String() 方法,该方法接收一个 Day 类型的参数,返回相应星期几的名字。
题目三
3、实现栈(stack)数据结构:
它的格子包含数据,比如整数 i、j、k 和 l 等等,格子从底部(索引 0)至顶部(索引 n)来索引。这个例子中假定 n=3,那么一共有 4 个格子。
一个新栈中所有格子的值都是 0。
push 将一个新值放到栈的最顶部一个非空(非零)的格子中。
pop 获取栈的最顶部一个非空(非零)的格子的值。现在可以理解为什么栈是一个后进先出(LIFO)的结构了吧。
为栈定义一 Stack 类型,并为它定义一个 Push 和 Pop 方法,再为它定义 String() 方法(用于调试)它输出栈的内容,比如:[0:i] [1:j] [2:k] [3:l]。
1)stack_arr.go:使用长度为 4 的 int 数组作为底层数据结构。
2)stack_struct.go:使用包含一个索引和一个 int 数组的结构体作为底层数据结构,索引表示第一个空闲的位置。
3)使用常量 LIMIT 代替上面表示元素个数的 4 重新实现上面的 1)和 2),使它们更具有一般性。
1)使用长度为 4 的 int 数组作为底层数据结构实现栈:
package main
import (
"fmt"
)
type Stack [4]int
func (s *Stack) Push(value int) {
for i, v := range s {
if v == 0 {
s[i] = value
return
}
}
}
func (s *Stack) Pop() int {
for i := len(s) - 1; i >= 0; i-- {
if s[i] != 0 {
value := s[i]
s[i] = 0
return value
}
}
return 0
}
func (s Stack) String() string {
var str string
for i, v := range s {
str += fmt.Sprintf("[%d:%d] ", i, v)
}
return str
}
func main() {
s := new(Stack)
s.Push(1)
s.Push(2)
s.Push(3)
s.Push(4)
fmt.Println(s)
fmt.Println(s.Pop())
fmt.Println(s.Pop())
fmt.Println(s)
}
解释:
在上面的代码中,我定义了一个 Stack 类型,它是一个长度为 4 的 int 数组的别名类型。我还为它定义了 Push() 和 Pop() 方法,以及 String() 方法。
在 Push() 方法中,我遍历栈中的元素,找到第一个空闲的位置,将新值插入到该位置。如果栈已满,该方法不做任何操作。
在 Pop() 方法中,我从栈顶开始遍历栈中的元素,找到第一个非空元素,返回它,并将该位置上的值清零。如果栈为空,该方法返回 0。
在 String() 方法中,我遍历整个栈,输出每个格子的索引和值,以字符串形式返回。
在 main() 函数中,我创建了一个 Stack 类型的变量 s,依次将值 1、2、3、4 压入栈中,打印栈的内容,然后弹出两个元素,再次打印栈的内容。
2)使用包含一个索引和一个 int 数组的结构体作为底层数据结构实现栈:
package main
import (
"fmt"
)
type Stack struct {
index int
data [4]int
}
func (s *Stack) Push(value int) {
if s.index < len(s.data) {
s.data[s.index] = value
s.index++
}
}
func (s *Stack) Pop() int {
if s.index > 0 {
s.index--
value := s.data[s.index]
s.data[s.index] = 0
return value
}
return 0
}
func (s Stack) String() string {
var str string
for i, v := range s.data {
str += fmt.Sprintf("[%d:%d] ", i, v)
}
return str
}
func main() {
s := new(Stack)
s.Push(1)
s.Push(2)
s.Push(3)
s.Push(4)
fmt.Println(s)
fmt.Println(s.Pop())
fmt.Println(s.Pop())
fmt.Println(s)
}
解释:
在上面的代码中,我定义了一个 Stack 结构体,它包含一个索引和一个长度为 4 的 int 数组。我还为它定义了 Push() 和 Pop() 方法,以及 String() 方法。
在 Push() 方法中,如果栈未满,我将新值插入到索引指向的位置,并将索引加一。
在 Pop() 方法中,如果栈非空,我将索引减一,并返回当前索引所指向的位置的值。如果栈为空,该方法返回 0。
在 String() 方法中,我遍历整个数组,输出每个格子的索引和值,以字符串形式返回。
在 main() 函数中,我创建了一个 Stack 类型的变量 s,依次将值 1、2、3、4 压入。
3)使用常量 LIMIT 代替上面表示元素个数的 4 重新实现上面的 1)和 2),使它们更具有一般性。
以下是使用常量 LIMIT 重构后实现的代码:
1)使用长度为 LIMIT 的 int 数组作为底层数据结构实现栈:
package main
import (
"fmt"
)
const LIMIT = 4
type Stack [LIMIT]int
func (s *Stack) Push(value int) {
for i, v := range s {
if v == 0 {
s[i] = value
return
}
}
}
func (s *Stack) Pop() int {
for i := len(s) - 1; i >= 0; i-- {
if s[i] != 0 {
value := s[i]
s[i] = 0
return value
}
}
return 0
}
func (s Stack) String() string {
var str string
for i, v := range s {
str += fmt.Sprintf("[%d:%d] ", i, v)
}
return str
}
func main() {
s := new(Stack)
s.Push(1)
s.Push(2)
s.Push(3)
s.Push(4)
fmt.Println(s)
fmt.Println(s.Pop())
fmt.Println(s.Pop())
fmt.Println(s)
}
解释:
在上面的代码中,我用 const 关键字定义了一个名为 LIMIT 的常量。然后,我使用 LIMIT 来代替之前直接写死的数组长度 4,使得代码更加具有一般性。
2)使用包含一个索引和一个 int 数组的结构体作为底层数据结构实现栈:
package main
import (
"fmt"
)
const LIMIT = 4
type Stack struct {
index int
data [LIMIT]int
}
func (s *Stack) Push(value int) {
if s.index < len(s.data) {
s.data[s.index] = value
s.index++
}
}
func (s *Stack) Pop() int {
if s.index > 0 {
s.index--
value := s.data[s.index]
s.data[s.index] = 0
return value
}
return 0
}
func (s Stack) String() string {
var str string
for i, v := range s.data {
str += fmt.Sprintf("[%d:%d] ", i, v)
}
return str
}
func main() {
s := new(Stack)
s.Push(1)
s.Push(2)
s.Push(3)
s.Push(4)
fmt.Println(s)
fmt.Println(s.Pop())
fmt.Println(s.Pop())
fmt.Println(s)
}
解释:
在上面的代码中,我同样使用 const 关键字定义了一个名为 LIMIT 的常量。然后,我用 LIMIT 来代替之前直接写死的数组长度,使得代码更加具有一般性。
总体来说,使用常量可以抽象出代码中的硬编码部分,让代码更具有可读性和可维护性。