什么是美?

在理工科领域,简单就是美。计算机软件领域也是一样。简单意味着易理解,不容易出Bug。

从0开始的数组下标

在计算机编程中数组的下标往往是从0开始,而老百姓熟悉的是从1开始的数字。按道理从1开始更自然更容易接受,也就意味着简单,可为什么多数的编程语言的数组是从零开始的呢?这个可不仅仅是习惯和语言设计者的个人的喜好的问题。

一句话,从0开始能够在许多方面带来运算的简单化。比如一维数组和二维数组的换算。如果我们规定下标,都从1开始,那么一维数组的下标就会是从1到9对应的二维数组的坐标就是(1,1)到(3,3)。两个数组的坐标对应如下。
为什么“含头不含尾”是科学的

而一维数组变成二维数组的坐标转换公式如下:
x = (i - 1) \ 3 + 1
y = (i - 1) % 3 + 1 = i % 3

(这里,\代表整除,即去掉商的小数部分。%代表取模,即得到余数。下同)

二维转一维的公式如下:
i = (x - 1) * 3 + (y - 1) + 1 = (x - 1) * 3 + y

但要是我们规定下标从0开始,对应的转换公式如下:

x = i \ 3
y = i % 3

i = x * 3 + y
为什么“含头不含尾”是科学的

倘若你觉得多出来几个+1/-1不算太麻烦,那么请看看一维和三维的转换
从1开始的公式:
x = (i - 1) \ 9 + 1
y = ((i - 1) % 9) \ 3 + 1
z = (i - 1) % 3 + 1
i = (x - 1) * 9 + (y - 1) * 3 + z
从0开始的公式:
x = i \ 9
y = (i % 9) \ 3
z = i % 3
i = x * 9 + y * 3 + z

不难发现,从0开始的坐标转换公式变得简单。同样的结论也适用于更高维的转换。

时间段的表示

在编程中,经常遇到要表示一段(连续)时间。比如某几天或者某个月,或者某几个月。通常的做法就是用两个时间点来表示这段时间,即是用这段时间的开始时间和结束时间来代表这一段时间。但同时也产生了“该不该包含开始时间点,该不该包含结束时间点”这样的分歧。比如2018年1月1日到2018年2月1日。有没有包含1月1日这一整天呢,这个大家还是有共识的,就是有。那么有没有包含2月1日这一天呢?这可不一定。

有一种观点是这样的,既然是到2月1日,那当然是包括2月1号这一天,如果不想包含这一天,那么应该是到1月31日。所以无论从界面上选择起始时间还是数据的传输保存都是包含最后的这一天的意思。我们可以称这种方式为“含头含尾”。
为什么“含头不含尾”是科学的
我们知道1.0等于1,1.00也等于1。同理,2018年1月1日等于2018年1月1日0:00:00。2018年2月1日等于2018年2月1日0:00:00。按含头含尾,当用这两个时间点来表示一段时间时。如果时间的单位是天,表示的是1月份这一整个月,外加2月1号这一整天。如果时间的单位是小时,表示的是1月份一整个月,加2月份的头1个小时。以此类推,也有可能表示的是加2月份的一分钟,或者可能是包含2月份的整个月。到底是取2月份的多长时间需要额外约定。传送数据时,一般只传两个时间点,而不传这个“额外的约定”,发送和接收双方一般是通过具体业务的理解推测这个单位,并且认为这是显而易见的,双方不容易发现对方与自己推测的不同,这导致埋下了一个可能产生Bug的坑。

假如我们规定表示的时间段包含开始的时间点,不包含结尾的时间点,简称为“含头不含尾”。那么很明确前面两个时间点表示的是完整的1月份。假如还要包含2月份的第一天,那么结尾的时间点就是2月2日。假如要包含2月份的第一个小时,那么结尾的时间点是2月1日 1:00。如果要包含2月份的整个月,那么结尾的时间点是3月1日。前面说过不写时分秒就等同于0时0分0秒。因为含头不含尾,所以并不包含结尾的这一秒在内。

由此可见,含头不含尾的表示方式,可以精确地表示任意的一个时间段,双方不需要额外约定时间的单位,做到了与具体的业务无关。一个方法可以同时处理不同的时间单位。这就是简单,这就是美。


人们在一眨眼间做出的 决策,其内涵远比表相 来的复杂。 《Bink眨眼之间》