Go语言实践[回顾]教程06--通过时间判断时辰的示例【上】
示例项目的需求
程序启动后,检测当前系统时间。根据时间不同,打印输出不同的信息内容:
● 23:00 点 ~ 1:00 点(不含1:00点),打印输出“现在是子时,俗称子夜”;
● 1:00 点 ~ 3:00 点(不含3:00点),打印输出“现在是丑时,俗称鸡鸣”;
● 3:00 点 ~ 5:00 点(不含5:00点),打印输出“现在是寅时,俗称黎明”;
● 5:00 点 ~ 7:00 点(不含7:00点),打印输出“现在是卯时,俗称破晓;
● 7:00 点 ~ 9:00 点(不含9:00点),打印输出“现在是辰时,俗称早晨;
● 9:00 点 ~ 11:00 点(不含11:00点),打印输出“现在是巳时,俗称上午;
● 11:00 点 ~ 13:00 点(不含13:00点),打印输出“现在是午时,俗称中午;
● 13:00 点 ~ 15:00 点(不含15:00点),打印输出“现在是未时,俗称日斜;
● 15:00 点 ~ 17:00 点(不含17:00点),打印输出“现在是申时,俗称日馎;
● 17:00 点 ~ 19:00 点(不含19:00点),打印输出“现在是酉时,俗称傍晚;
● 19:00 点 ~ 21:00 点(不含21:00点),打印输出“现在是戌时,俗称黄昏;
● 21:00 点 ~ 23:00 点(不含23:00点),打印输出“现在是亥时,俗称深夜;
实现示例需求的源代码
实现一个项目需求,代码逻辑往往有很多种,下面就从最初实现到逐步优化的角度实践一下这个项目。
使用 if 判断逻辑实现的源代码
// test01 project main.go
package main
import (
"fmt"
"time"
)
var now = time.Now().Hour() // 返回的是int类型
func main() {
if now >= 23 || now < 1 {
fmt.Println("现在是子时,俗称子夜")
}
if now >= 1 && now < 3 {
fmt.Println("现在是丑时,俗称鸡鸣")
}
if now >= 3 && now < 5 {
fmt.Println("现在是寅时,俗称黎明")
}
if now >= 5 && now < 7 {
fmt.Println("现在是卯时,俗称破晓")
}
if now >= 7 && now < 9 {
fmt.Println("现在是辰时,俗称早晨")
}
if now >= 9 && now < 11 {
fmt.Println("现在是巳时,俗称上午")
}
if now >= 11 && now < 13 {
fmt.Println("现在是午时,俗称中午")
}
if now >= 13 && now < 15 {
fmt.Println("现在是未时,俗称日斜")
}
if now >= 15 && now < 17 {
fmt.Println("现在是申时,俗称日馎")
}
if now >= 17 && now < 19 {
fmt.Println("现在是酉时,俗称傍晚")
}
if now >= 19 && now < 21 {
fmt.Println("现在是戌时,俗称黄昏")
}
if now >= 21 && now < 23 {
fmt.Println("现在是亥时,俗称深夜")
}
}
上述代码编译运行,输出结果是正确的。下面解读一下代码中较之前示例新增的内容:
● 第9行,now 变量的值是 int 类型,因为 time.Now().Hour() 函数返回的是 0 ~ 23 的整数类型,表示当前时间是几点。与 time.Now().Hour() 类似的还有获取、年、月、日、分、秒等函数,在本节总结中会列出,还有常见的数据类型。
● 第12 ~ 47行,使用了12个 if 判断语句。可以看出,Go 语言的 if 判断语句省略了包裹判断条件的括号。与函数的格式一样,包裹条件成立执行代码块的花括号中的左花括号 { ,必须与 if 关键字及条件在同一行且必须在行尾。
● 第12行,判断条件有两个,使用的是 || 逻辑运算符,是 或 的关系。now 在第9行获得的最大值是23,最小值是0。所以判断子时的条件应该是大于等于23(也可以是等于 == 逻辑运算符,因为没有24和更大值了)或者小于1(因为需求中要求23点至1点之间,不包括1点整,为子时)。
● 第15行,判断条件也有两个,但是使用的是 && 逻辑运算符,是 并且 的关系。因为需求中要求1点至3点(不含3点),所以应该是大于等于1并且小于3(而不是小于等于3)。后面的几个判断条件原理相同。
使用 if else if 判断逻辑实现的源代码
在上面代码中,12个判断是并列依次执行的。也就是无论如何12个判断都要执行一遍,即使第一个已经成立,那后面的11个判断依然要执行。每次运行程序,其实只能有一个判断的条件会成立,那这样也12个判断都要执行一遍的逻辑显然不是最优的选择。那么该如何优化呢?看下面的代码:
// test01 project main.go
package main
import (
"fmt"
"time"
)
var now = time.Now().Hour() // 返回的是int类型
func main() {
if now >= 1 && now < 3 {
fmt.Println("现在是丑时,俗称鸡鸣")
} else if now >= 3 && now < 5 {
fmt.Println("现在是寅时,俗称黎明")
} else if now >= 5 && now < 7 {
fmt.Println("现在是卯时,俗称破晓")
} else if now >= 7 && now < 9 {
fmt.Println("现在是辰时,俗称早晨")
} else if now >= 9 && now < 11 {
fmt.Println("现在是巳时,俗称上午")
} else if now >= 11 && now < 13 {
fmt.Println("现在是午时,俗称中午")
} else if now >= 13 && now < 15 {
fmt.Println("现在是未时,俗称日斜")
} else if now >= 15 && now < 17 {
fmt.Println("现在是申时,俗称日馎")
} else if now >= 17 && now < 19 {
fmt.Println("现在是酉时,俗称傍晚")
} else if now >= 19 && now < 21 {
fmt.Println("现在是戌时,俗称黄昏")
} else if now >= 21 && now < 23 {
fmt.Println("现在是亥时,俗称深夜")
} else {
fmt.Println("现在是子时,俗称子夜")
}
}
上述代码编译运行,输出结果是正确的。
经过这样优化后,12个独立的判断就合并成一组判断,只要有一个条件成立,就不会再执行这组中的其它判断,很显然,是 else 关键字的作用,这与其他大部分编程语言基本一致。
第14、16、18、20、22、24、26、28、30、32行,都是使用的关键字 else if,意思是前面的条件如果不成立的话继续判断本行的条件是否成立。
第34行,仅使用 else 关键字,意思是前面的条件不成立,那么就执行这里的代码。
使用 switch case 判断式逻辑实现的源代码
在上面代码中,依然有12个 if 判断式,能不能简化一下使代码更易读呢?看下面的代码:
// test01 project main.go
package main
import (
"fmt"
"time"
)
var now = time.Now().Hour() // 返回的是int类型
func main() {
switch {
case now >= 1 && now < 3:
fmt.Println("现在是丑时,俗称鸡鸣")
case now >= 3 && now < 5:
fmt.Println("现在是寅时,俗称黎明")
case now >= 5 && now < 7:
fmt.Println("现在是卯时,俗称破晓")
case now >= 7 && now < 9:
fmt.Println("现在是辰时,俗称早晨")
case now >= 9 && now < 11:
fmt.Println("现在是巳时,俗称上午")
case now >= 11 && now < 13:
fmt.Println("现在是午时,俗称中午")
case now >= 13 && now < 15:
fmt.Println("现在是未时,俗称日斜")
case now >= 15 && now < 17:
fmt.Println("现在是申时,俗称日馎")
case now >= 17 && now < 19:
fmt.Println("现在是酉时,俗称傍晚")
case now >= 19 && now < 21:
fmt.Println("现在是戌时,俗称黄昏")
case now >= 21 && now < 23:
fmt.Println("现在是亥时,俗称深夜")
default:
fmt.Println("现在是子时,俗称子夜")
}
}
上述代码编译运行,输出结果是正确的。
这次简化,使用 switch 判断取代了 if 判断,运行的结果是一样的。
第12行,使用了 switch 关键字,且后面没有变量,直接是左花括号。这表示后面的判断分支 case 将是布尔值类型的值,真为条件成立,假为条件不成立。
Go 语言的 switch 判断与其他语言类似,也是有一个 case 条件满足就不会再去判断其他 case,但也有些差别。switch 关键字后面可以没有变量,这时每个 case 后面就是布尔值(所以可以是判断式)为真则执行本段代码;当 switch 关键字后面有变量的时候, case 后面就是对应的值,变量等于该值则执行本段代码。default 的作用是所有条件都不成立时执行此段代码。
使用 switch 变量 case 值逻辑实现的源代码
在上面代码中,与使用 if else if 相差不是很大,我们还可以使用 switch 变量 case 值 的形式进行判断,代码如下:
// test01 project main.go
package main
import (
"fmt"
"time"
)
var now = time.Now().Hour() // 返回的是int类型
func main() {
switch now {
case 1, 2:
fmt.Println("现在是丑时,俗称鸡鸣")
case 3, 4:
fmt.Println("现在是寅时,俗称黎明")
case 5, 6:
fmt.Println("现在是卯时,俗称破晓")
case 7, 8:
fmt.Println("现在是辰时,俗称早晨")
case 9, 10:
fmt.Println("现在是巳时,俗称上午")
case 11, 12:
fmt.Println("现在是午时,俗称中午")
case 13, 14:
fmt.Println("现在是未时,俗称日斜")
case 15, 16:
fmt.Println("现在是申时,俗称日馎")
case 17, 18:
fmt.Println("现在是酉时,俗称傍晚")
case 19, 20:
fmt.Println("现在是戌时,俗称黄昏")
case 21, 22:
fmt.Println("现在是亥时,俗称深夜")
case 23, 0:
fmt.Println("现在是子时,俗称子夜")
}
}
上述代码编译运行,输出结果是正确的。
这次修改仍然使用 switch 关键字,区别是使用了swith 的另一种格式。
第12行,在 switch 关键字后面加入了 now 变量,这里与大部分编程语言就几乎一样了。注意:这里变量不需要用括号包起来。
第13、15、17、19、21、23、25、27、29、31、33、35行,case 关键字后面可以是多个值,这样就相当于是 或 的关系,也就是变量 now 的值等于 case 后面的其中任何一个值都算条件成立,都会执行该段代码。这与大部分编程语言不一样,Go 语言这个比较灵活方便。
这样修改后,感观上代码清晰了不少。
减少 fmt.Println() 函数使用次数的源代码
在上面代码中,fmt.Println() 函数被使用了12次,且都是在判断结构中的同一位置,是否可以减少使用次数呢?看下面代码:
// test01 project main.go
package main
import (
"fmt"
"time"
)
var now = time.Now().Hour() // 返回的是int类型
func main() {
var info string
switch now {
case 1, 2:
info = "现在是丑时,俗称鸡鸣"
case 3, 4:
info = "现在是寅时,俗称黎明"
case 5, 6:
info = "现在是卯时,俗称破晓"
case 7, 8:
info = "现在是辰时,俗称早晨"
case 9, 10:
info = "现在是巳时,俗称上午"
case 11, 12:
info = "现在是午时,俗称中午"
case 13, 14:
info = "现在是未时,俗称日斜"
case 15, 16:
info = "现在是申时,俗称日馎"
case 17, 18:
info = "现在是酉时,俗称傍晚"
case 19, 20:
info = "现在是戌时,俗称黄昏"
case 21, 22:
info = "现在是亥时,俗称深夜"
case 23, 0:
info = "现在是子时,俗称子夜"
}
fmt.Println(info)
}
上述代码编译运行,输出结果是正确的。
这次修改增加了第12行的内容,声明了一个字符串类型的局部变量 info,没有初始化赋值,那默认就是空字符串。然后在下面每个 case 里分别给变量 info 赋不同的值,最后在第41行使用一次 fmt.Println(info),就可以输出不同的信息了,因为实际打印输出的是变量 info 里的内容,而 info 里的内容是上面根据判断赋值的,不是一成不变的。
减少多次重复出现的字符串字面量的源代码
在上面代码中,虽然减少了fmt.Println() 函数的使用次数,但可以看出“现在是”、“,俗称”重复出现了12次,且也都是在判断结构中的同一位置,是否也可以减少使用次数呢?看下面代码:
// test01 project main.go
package main
import (
"fmt"
"time"
)
var now = time.Now().Hour() // 返回的是int类型
func main() {
var shiChen, suCheng string
switch now {
case 1, 2:
shiChen = "丑时"
suCheng = "鸡鸣"
case 3, 4:
shiChen = "寅时"
suCheng = "黎明"
case 5, 6:
shiChen = "卯时"
suCheng = "破晓"
case 7, 8:
shiChen = "辰时"
suCheng = "早晨"
case 9, 10:
shiChen = "巳时"
suCheng = "上午"
case 11, 12:
shiChen = "午时"
suCheng = "中午"
case 13, 14:
shiChen = "未时"
suCheng = "日斜"
case 15, 16:
shiChen = "申时"
suCheng = "日馎"
case 17, 18:
shiChen = "酉时"
suCheng = "傍晚"
case 19, 20:
shiChen = "戌时"
suCheng = "黄昏"
case 21, 22:
shiChen = "亥时"
suCheng = "深夜"
case 23, 0:
shiChen = "子时"
suCheng = "子夜"
}
info := "现在是" + shiChen + ",俗称" + suCheng
fmt.Println(info)
}
上述代码编译运行,输出结果是正确的。
这次修改是在第12行声明定义了两个字符串局部变量 shiChen 和 suCheng,分别代表 时辰名 和 俗称,声明后没有初始化,会默认为空字符串,然后在下面的每个 case 里分别给这两个变量赋相应的字符串值。到第53行,将 “现在是”、变量 shiChen、“,俗称”、变量 suCheng 四个字符串拼接到一起后,赋值给新声明的变量 info,然后在第55行输出这个最后保存合并内容的变量 info,就得到了想要的完整信息内容。Go 语言中字符串拼接可以直接使用加号 “+” 完成。
也可以将第53行等号右侧的代码复制,然后替换掉第55行函数中的参数 info,这样就可以删除53行代码,程序执行结果也是正常的。
本节对“通过时间判断时辰的示例”先探讨到这里,下一节中将继续实践。
本节小结
下是对本节涉及的 Go 语言编程内容的归纳总结,方便记忆:
● 基础数据类型见下表(本例中没有用到的也一并列出了)
类型 | 名 | 值 | 说明 |
---|---|---|---|
bool | 布尔 | true / false | 通常代表 真/假、有/无、是/否、1/0等 |
string | 字符串 | 字节序列 | UTF-8 字符序列,ASCII 码表中字符占 1 个字节,其它字符占用 2-4 个字节,汉字三个字节 |
int | 整数 | 32位 或 64位 有符号 | 有正负数 ,长度随系统 |
int8 | 整数 | 8位 有符号 | 有正负数 ,长度8位,-128 ~ 127 |
int16 | 整数 | 16位 有符号 | 有正负数 ,长度16位,-32768 ~ 32767 |
int32 | 整数 | 32位 有符号 | 有正负数 ,长度32位,-2147483648 ~ 2147483647 |
int64 | 整数 | 64位 有符号 | 有正负数 ,长度64位,-9223372036854775808 ~ 9223372036854775807 |
uint | 整数 | 32位 或 64位 无符号 | 无负数 ,长度随系统 |
uint8 | 整数 | 8位 无符号 | 无负数 ,长度8位,0 ~ 255 |
uint16 | 整数 | 16位 无符号 | 无负数 ,长度16位,0 ~ 65535 |
uint32 | 整数 | 32位 无符号 | 无负数 ,长度32位,0 ~ 4294967295 |
uint64 | 整数 | 64位 无符号 | 无负数 ,长度64位,0 ~ 18446744073709551615 |
uintptr | 整数 | 无符号 | 无负数 ,一个能足够容纳指针位数大小的整数类型 |
byte | 字节 | 8位 无符号 | uint8 的别名 ,用于表示一个字节 |
rune | 符文 | 32位 无符号 | uint32 的别名 ,用于表示一个Unicode 码 |
float32 | 单精度 | 32位 有符号 | 单精度浮点数,占用32位长度 |
float64 | 双精度 | 64位 有符号 | 双精度浮点数,占用64位长度 |
complex64 | 64位复数 | 64位 有符号 | 由两个float32构成,一个表示实部,一个表示虚部 |
complex128 | 128位复数 | 128位 有符号 | 由两个float64构成,一个表示实部,一个表示虚部 |
● 变量声明时如果没有初始化赋值,那么默认布尔类型为 false,字符串类型为 空字符串,整数类型为 0 ,浮点数类型为 0.0,指针类型为 nil(空值,也就是未赋值的意思)。
● time包中类似 time.Now().Hour() 的常用函数有以下这些:
函数 | 返回值 | 功能说明 |
---|---|---|
time.Now().Year() | 2022等 | 获取当前年份 |
time.Now().Month() | 1 ~ 12 | 获取当前月份 |
time.Now().Day() | 1-31 | 获取当前日期 |
time.Now().Hour() | 0-23 | 获取当前小时 |
time.Now().Minute() | 0-59 | 获取当前分钟 |
time.Now().Second() | 0-59 | 获取当前秒 |
● 逻辑判断 if 与 if else 及 if else if 也是判断 if 后面的条件是否成立,如果成立就执行紧跟其后的花括号包裹的代码段。但是条件部分不需要用小括号了,且左花括号必须与 if 关键字及条件表达式在同一行。else 也是表示上面判断不成立则执行这里的代码段的含义。
● 逻辑判断 swith case 除了 swith 变量,然后用户 case 值 匹配这种写法外,还可以 swith 不带任何变量,在 case 后面写逻辑表达式或布尔值,哪个条件成立或为真就进入哪个代码段,其他的 case 不在去执行匹配判断。在使用 swith 变量 这种格式时,变量也不需要用括号包起来。
● 多个字符串拼接直接使用加号 + 即可按照顺序拼接成一个新的字符串。
● 代码中多次出现重复的代码,是可以优化掉的,通常通过创建变量、函数、包的办法,然后共用他们。
.
.
上一节:Go/Golang语言学习实践[回顾]教程05–深入实践第一个Go语言示例