Go语言实践[回顾]教程07--通过时间判断时辰的示例【中】

继续修改上节的代码

  经过上节的几次调整修改,代码越来越逻辑清晰易读,但依旧还有很大的修改空间 (咦!为什么不说优化,因为优化是针对特定情况进行的修改,不是所有的修改都一定是优化,要看具体针对的场景,所以用修改一词无争议)。目的是经过使用不同的编程方式方法,实践到更多的 Go 语言基础知识,掌握更多编程技巧,体验编程的魅力!

使用数组存储时辰和俗称的源代码

// test01 project main.go
package main

import (
	"fmt"
	"time"
)

func main() {
	var ( // 批量声明的语法
		now  = time.Now().Hour()
		name = [24]string{ // 声明数组变量并初始化
			"子时", "子夜",
			"丑时", "鸡鸣",
			"寅时", "黎明",
			"卯时", "破晓",
			"辰时", "早晨",
			"巳时", "上午",
			"午时", "中午",
			"未时", "日斜",
			"申时", "日馎",
			"酉时", "傍晚",
			"戌时", "黄昏",
			"亥时", "深夜", // 最后这个逗号如果省略,那就必须将右花括号移上拉
		}
		num int
	)

	switch now {
	case 23, 0:
		num = 0
	case 1, 2:
		num = 2
	case 3, 4:
		num = 4
	case 5, 6:
		num = 6
	case 7, 8:
		num = 8
	case 9, 10:
		num = 10
	case 11, 12:
		num = 12
	case 13, 14:
		num = 14
	case 15, 16:
		num = 16
	case 17, 18:
		num = 18
	case 19, 20:
		num = 20
	case 21, 22:
		num = 22
	}

	fmt.Println("现在是" + name[num] + ",俗称" + name[num+1])
}

  上述代码编译运行,输出结果是正确的。

  这次修改取消了全局变量的声明,改用局部变量批量声明的方式。取消了在 swith 语句的 case 中给两个变量直接赋值时辰名、俗称名的字符串,改为在 case 中确定索引值,使用数组预置所有时辰名和俗称名的方式。

  第10~27行,是批量声明变量,var 后面的括号内,以每个变量占一行的形式,可以同时定义多个变量,不需要每个变量都要写个 var 了,变量之间也不需要逗号分割。不能整体写在一行上,右括号必须单独占一行(可以在最后一个变量的尾部,但是格式化工具也会修改成上面代码所示的格式)。

  第12~25行,是声明定义了一个数组变量 name,[24] 表示是具有24个元素的数组,string表示数组元素是字符串类型的。左花括号必须与类型在同一行(这是Go语言的一个语法特点),数组元素和右花括号可以与左花括号在同一行,也可以分开多行。各元素之间用逗号分开,如果右花括号紧跟最后一个元素在同一行,那么最后一个元素后面不用逗号,如果右花括号单独占一行,最后一个元素后面就必需有逗号

  第31、33、35、37、39、41、43、45、47、49、51、53行,给变量 num 赋值的是该时间对应在数组 name 中的 时辰名 的索引号(第一个元素的索引号是 0),因为还有 俗称名,所以各值之前就隔了一个数。

  第56行,将 fmt.Println() 函数的参数改成一个字符串拼接的表达式,name[num] 是表示获取数组 name 中的 num 号索引的元素,num 的值是在上面的 switch 语句中各 case 内赋值的,这样当前时间与数组的索引就有了对应关系。name[num+1] 也是表示获取数组 name 的元素,索引值是 num+1 后的结果。为什么要这样呢?我们看数组声明是24个元素,每两个元素对应一个时辰。这两个元素一个是时辰名,一个是俗称名,且俗称名刚好比时辰名索引号大1。而在 swith 语句中 num 被赋值的是 时辰名的位置,因此有了加一的逻辑。

  可能有些同学会有个问题,明明是12个时辰, swith 语句也是12个分支,能不能让数组也是12个元素这样一一对应,是不是更容易理解呢?好,那我们就继续修改!

使用二维数组明确与12时辰对应关系的源代码

// test01 project main.go
package main

import (
	"fmt"
	"time"
)

func main() {
	var ( // 批量声明的语法
		now  = time.Now().Hour()
		name = [12][2]string{ // 声明二维数组变量并初始化
			{"子时", "子夜"},
			{"丑时", "鸡鸣"},
			{"寅时", "黎明"},
			{"卯时", "破晓"},
			{"辰时", "早晨"},
			{"巳时", "上午"},
			{"午时", "中午"},
			{"未时", "日斜"},
			{"申时", "日馎"},
			{"酉时", "傍晚"},
			{"戌时", "黄昏"},
			{"亥时", "深夜"},
		}
		num int
	)

	switch now {
	case 23, 0:
		num = 0
	case 1, 2:
		num = 1
	case 3, 4:
		num = 2
	case 5, 6:
		num = 3
	case 7, 8:
		num = 4
	case 9, 10:
		num = 5
	case 11, 12:
		num = 6
	case 13, 14:
		num = 7
	case 15, 16:
		num = 8
	case 17, 18:
		num = 9
	case 19, 20:
		num = 10
	case 21, 22:
		num = 11
	}

	fmt.Println("现在是" + name[num][0] + ",俗称" + name[num][1])
}

  上述代码编译运行,输出结果是正确的。

  这次修改将数组由一维数组改成了二维数组,将每个 case 对 num 变量跳跃隔1赋值改为顺序号赋值,这样就与数组定义的元素索引(顺序号)一一对应了。

  第12~25行,修改为声明定义了一个二维数组变量 name。[12] 表示这个数组第一维有12个元素,[2] 表示每个元素还是个具有2个元素的数组(第二维),string 表示第二维里的元素是字符串类型。第一维的每个元素要用花括号包起来(因为元素还是数组),各元素之间依然需要逗号分开。第二维里面就与上面的一维数组一样了,其他注意事项与上面的一维数组相同。

  第31、33、35、37、39、41、43、45、47、49、51、53行,修改为给变量 num 赋值的是数组 name 第一维的索引号,所以各值之间是连续的。这样实际值就是在 0~11 之间,刚好与 name 数组的第一维索引号一一对应了。

  第56行,将 fmt.Println() 函数的参数的字符串拼接的表达式中的变量部分,分别改成 name[num][0] 和 name[num][1],name[num][0] 是表示获取数组 name 的 num 号元素中的0号元素,name[num][1] 是表示获取数组 name 的 num 号元素中的1号元素。这样就用同一个 num 值,同时获得了 时辰名 和 俗称名,而不需要加1运算了。

  现在我们再仔细看代码,是不是感觉 swith 语句占用的较大篇幅,且作用就是赋值给 num 一个有规律的顺序号,可不可以将 swith 语句用简单点的语句替换掉呢?好的,我们继续实践修改!

去掉 swith 语句改用计算公式的源代码

// test01 project main.go
package main

import (
	"fmt"
	"time"
)

func main() {
	var ( // 批量声明的语法
		now  = time.Now().Hour()
		name = [12][2]string{ // 声明二维数组变量并初始化
			{"子时", "子夜"},
			{"丑时", "鸡鸣"},
			{"寅时", "黎明"},
			{"卯时", "破晓"},
			{"辰时", "早晨"},
			{"巳时", "上午"},
			{"午时", "中午"},
			{"未时", "日斜"},
			{"申时", "日馎"},
			{"酉时", "傍晚"},
			{"戌时", "黄昏"},
			{"亥时", "深夜"},
		}
		num int
	)

	if now%2 == 0 { // 是偶数
		num = now / 2
	} else if now == 23 { // 是奇数且等于23
		num = 0
	} else { // 是奇数且不等于23
		num = (now + 1) / 2
	}

	fmt.Println("现在是" + name[num][0] + ",俗称" + name[num][1])
}

  上述代码编译运行,输出结果是正确的。

  若想去掉对每个时间的逐一判断,那就要找到时间与顺序号的对应规律。在前面的代码中,我们可以看到,除了23点,其他的时间若是偶数的,直接除以 2,得数就是顺序号;若是奇数的,先加 1 后再除以 2,得数也是顺序号。根据这个规律,我们就有了修改思路:
  先判断当前时间 now 是否是偶数?
  如果是偶数,就将当前时间 now 除以 2,结果赋值给代表顺序号的变量 num;
  如果是奇数,再判断 now 是否等于 23 ?
    如果等于 23,那就让顺序号变量 num 等于 0;
    如果不等于 23,那就将当前时间 now 先加上1,然后再除以 2,最后将结果赋值给顺序号变量 num。

    第29~35行,就是根据上面思路编写的代码。if now%2 == 0 表示 now变量的值除以2后的余数等于 0这个条件是否成立。如果成立说明没有余数,那就是偶数,否则就是奇数。% 是求余数的运算符。大部分编程语言中,乘除的运算都比较影响运行效率,所以判断是否是偶数还可以写成 if num&1 == 0 这样,& 是二进制位运算符 与运算,对于位运算,因为计算机底层指令都是二进制运算,所以普遍运算效率要高很多,不熟悉二进制位运算的可以先不用,但无需质疑。

本节小结

  以下是对本节涉及的 Go 语言编程内容的归纳总结,方便记忆:

  ● Go 语言的常见运算符整理如下:
  算术运算符

运算符说明
+a + b,两边都是数字则为相加,都是字符串则是拼接
-a - b,两数相减
*a * b,两数相乘
/a / b,两数相除,两数中有任何一个是浮点数,结果则为浮点数,否则结果只保留整数部分
%a % b,求两数相除后的余数
++a++,自身值增加1
a–,自身值减少1

  关系运算符

运算符说明
==a == b,如果 a 等于 b 则返回 True,否则返回 False
!=a != b,如果 a 不等于 b 则返回 True,否则返回 False
>a > b,如果 a 大于 b 则返回 True,否则返回 False
>=a >= b,如果 a 大于或等于 b 则返回 True,否则返回 False
<a < b,如果 a 小于 b 则返回 True,否则返回 False
<=a <= b,如果 a 小于或等于 b 则返回 True,否则返回 False

  逻辑运算符

运算符说明
&&a && b,如果 a 为 True 并且 B 为 True,则条件为 True,否则条件为 False
||a || b,如果 a 为 True 或者 B 为 True,则条件为 True,否则条件为 False
!!a,如果 a 为 True,则条件反转为 False;如果 a 为 False,则条件反转为 True

  位运算符

运算符说明(假设 a = 3 二进制表示 00000011,b = 5 二进制表示 00000101)
&按位与运算,两数二进制同位都为 1 结果该位为 1,否则为 0。a & b 结果为 1 (00000001)
|按位或运算,两数二进制同位有一个为 1 结果该位就为 1,都是 0 为 0。a | b 结果为 7 (00000111)
^按位异或运算,两数二进制同位不一样则结果该位就为 1,否则为 0。a ^ b 结果为 6 (00000110)
<<按位左移运算符,a<< 1 结果为 6 (00000110) ,b << 1 结果为 10 (00001010)
>>按位右移运算符,a >> 1 结果为 1 (00000001) ,b >> 1 结果为 2 (00000010)

  赋值运算符

运算符说明
=简单赋值运算,将右侧的值赋给左侧
+=左侧与右侧相加后再赋给左侧。a += b, 等同于 a = a + b
-=左侧与右侧相减后再赋给左侧。a -= b, 等同于 a = a - b
*=左侧与右侧相乘后再赋给左侧。a *= b, 等同于 a = a * b
/=左侧与右侧相除后再赋给左侧。a /= b, 等同于a = a / b
%=左侧与右侧相除取余后再赋给左侧。a %= b, 等同于 a =a % b
&=左侧与右侧按位相与后再赋给左侧。a &= b, 等同于 a = a & b
|=左侧与右侧按位相或后再赋给左侧。a |= b, 等同于 a = a | b
^=左侧与右侧按位异或后再赋给左侧。a ^= b, 等同于 a = a ^ b
<<=左侧左移右侧值那么多位后再赋给左侧。a <<= 3, 等同于 a = a << 3
>>=左侧右移右侧值那么多位后再赋给左侧。a >>= 3, 等同于 a = a >> 3

  指针类运算符

运算符说明
&返回变量的存储地址而不是变量的值。&a,这将得到变量 a 的实际地址
*定义指针变量。*a,表示 a 是一个指针变量

  运算符的优先级(优先级数字越大级别越高)

优先级分类运算符(同类优先级为从左至右)
14后缀运算( )、[ ]
13单目运算-(负号)、+(正号)、–、++、& 、*(指针)、!
12乘除法/取余*(乘号)、/、%
11加减法+、-
10位移运算<<、>>
9关系运算<、<=、>、>=
8等于不等==、!=
7按位与&
6按位异或^
5按位或|
4逻辑关系与&&
3逻辑关系或||
2赋值运算|=、^=、&=、<=、>=、%=、/=、*=、-=、+=、=

  ● 一维数组声明定义格式如下:
  var a [12]string 这是定义了一个有12个字符串类型元素的数组变量 a,没有初始化赋值。
  var a = [3]int{10, 26, 19} 这是定义了一个有3个int类型元素的数组,元素值分别是10、26、19。
  var a = [5]int{10, 26, 19} 这是定义了一个有5个int类型元素的数组变量 a,元素值从前至后分别是10、26、19,余下两个元素默认为 0(字符串类型的则为空字符串)。
  a := [3]int{10, 26, 19} 这是定义了一个有3个int类型元素的数组,元素值分别是10、26、19。这种语法仅适用于局部变量。
  a[0] 表示获取数组a的第一个元素。

  ● 二维数组声明定义格式如下:
  var b [3][2]int 这是定义了一个第一维有3个元素,第二维有2个int类型元素的二维数组变量 b,没有初始化赋值。
  var b [3][2]int{{11, 15}, {21, 26}, {36, 38}} 这是定义了一个第一维有3个元素,第二维有2个int类型元素的二维数组变量 b,并初始化赋值了。其他特点与一维数组相同。

  ● 使用 var () 可以批量声明变量,每个变量占一行,不需要逗号分隔。
.
.
上一节:Go/Golang语言学习实践[回顾]教程06–通过时间判断时辰的示例【上】

下一节:Go/Golang语言学习实践[回顾]教程08–通过时间判断时辰的示例【下】
.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学为所用

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值