如何阅读包含很多变量的代码?

举个例子,下面这块golang代码的的作用是负责把rgb色彩转化为hsl色彩。

package main

import (
	"fmt"
	"math"
)

func rgb2hsl(r int, g int, b int) (int, int, int) {
	var rf, gf, bf, max, min, l, d, s, h float64

	rf = math.Max(math.Min(float64(r)/255, 1), 0)
	gf = math.Max(math.Min(float64(g)/255, 1), 0)
	bf = math.Max(math.Min(float64(b)/255, 1), 0)
	max = math.Max(rf, math.Max(gf, bf))
	min = math.Min(rf, math.Min(gf, bf))
	l = (max + min) / 2

	if max != min {
		d = max - min
		if l > 0.5 {
			s = d / (2 - max - min)
		} else {
			s = d / (max + min)
		}
		if max == rf {
			if gf < bf {
				h = (gf-bf)/d + 6
			} else {
				h = (gf - bf) / d
			}
		} else if max == gf {
			h = (bf-rf)/d + 2
		} else {
			h = (rf-gf)/d + 4
		}
	} else {
		h = 0
		s = 0
	}

	return int(h * 60), int(s * 100), int(l * 100)
}

func main() {
	fmt.Println(rgb2hsl(121, 167, 22))
	fmt.Println(rgb2hsl(69, 209, 237))
	fmt.Println(rgb2hsl(254, 207, 37))
	fmt.Println(rgb2hsl(122, 167, 255))
	fmt.Println(rgb2hsl(255, 255, 255))
}

乍一看变量乱七八糟,命名也不规范,读起来可能就有点难受。而且我们也不假设在我们读代码的时候,一定有关于这些代码的先验知识,当然有这个先验知识虽然不见得是必要的,但是是很有帮助的,所以如果可能,要尽量先获得这个先验知识,例如这个例子中,如果你对rgb和hsl颜色的原理理解得越好,并且越清楚它们之间的转换关系,那么读懂代码就越容易。不过如果没有这种先验知识,是否就不能读懂了呢?当然不是,如果实在没办法,硬着头皮也是可以啃下来的,但是注意方法始终是必要的,当函数中的变量很多时,可能容易被搞晕。我总结的一个技巧是画“变量依赖图”。具体步骤如下:

函数体再大都先不要看,只看最后return 语句,看看返回了哪些变量,比如上面的例子中,只包含了h, s, l这三个变量,那么往上顺藤摸瓜,看看h,s,l的计算依赖哪些其他变量呢?例如h依赖了bf, d, rf 三个变量, s依赖了max, d, min三个变量,等等。那么以此类推就可以把整个函数体内所有涉及到的变量的依赖关系都画出来了,如下图:

可以看到在没有这张图的情况下,凭空脑子里想这样一个结构是要多费很多精力的,这个图还算简单的例子,如果有更复杂的依赖关系,那么这样的图一旦画出来,对理解的帮助更大,因为依赖往往不是线性的,而是网状的。画图时还可以把同类型的变量用相同颜色标出来,这样可以更方便地帮助理解,例如max和min就是同层级的变量,gf, rf, bf也是同层次的变量,那么就可以分别用相同的颜色来标志。如果需要,每个节点上还可以加一些注释,例如d这个节点,老实说这种变量命名是很不规范的,但是我们读代码时真的无法保证别人写的代码都是规范的,所以有时候在旁边加个注释就对帮助理解很有必要,我这里没加,不过看源码寻找d的来源可以知道d代表的是max和min的距离。

个人感觉这种方法是对问题的一个拆解和具象化,可以把一个复杂的问题化为更小更简单的问题,所谓的分而治之。

不过,用这种方法的时候,画图本身也是需要时间的,所以是否要这样做,需要具体权衡,以省时间为标准。

猜你喜欢

转载自blog.csdn.net/xunileida/article/details/83215315