为什么“含头不含尾”是科学的

什么是美?

在理工科领域,简单就是美。计算机软件领域也是一样。简单意味着易理解,不容易出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秒。因为含头不含尾,所以并不包含结尾的这一秒在内。

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

原文地址:https://www.cnblogs.com/BillySir/p/10123975.html

时间: 2024-10-31 22:19:04

为什么“含头不含尾”是科学的的相关文章

(含头指针以及尾指针)循环双向链表各类功能的实现

对循环双链表实现下述功能: void meau(); //菜单函数 void Initlist(List *list); //初始化 void show(List *list); //打印链表内容 bool Push_back(List *list,ElemType x); //尾插法 bool Push_front(List *list,ElemType x);//头插法 bool Isempty(List *list); //判断链表是否为空 bool Pop_back(List *list

【数据结构】用C++实现顺序表的各种操作(包括头删,尾删,插入,逆序,摧毁,清空等等)

//顺序表的各种操作(包括头删,尾删,插入,逆序,摧毁,清空等等) //头文件 #pragma once #include <iostream> using namespace std; template<class Type> class SeqList { public: SeqList(size_t sz = INIT_SIZE); ~SeqList(); public: bool isfull() const {return size >= capacity; } b

双循环链表(包含头指针与尾指针)

双循环链表(包含头指针与尾指针) 以及基本功能的实现 list_ d_link_c.h #ifndef _LIST_D_LINK_C_ #define _LIST_D_LINK_C_ #include<iostream> #include<assert.h> using namespace std; #define ElemType int typedef struct Node { ElemType data; struct Node *prio; struct Node *ne

链表之 头节点与尾指针 区别

单链表的必要条件:头节点的head,以及尾指针指向null: 1 头结点 首先,不要被以下三个词组弄混了: 链表头:数据内容为第一个元素的结点. 头指针:指向头结点元素的指针. 头结点:数据内容无效,其指针是头指针. 一句话描述为:头指针是指向头结点的指针,头结点是指向链表头的结点. 对于一个链表来说,头指针是一定存在的,是访问链表的入口,如果没有头指针则无法对其进行访问:链表头对于非空表来说是一定存在的,非空表则不存在. 2 尾指针 另外一种链表的技巧是使用尾指针. 尾指针是相对于头指针而言的

手把手教你给RecycleView添加头布局和尾布局

RecycleView想必大家都不陌生,它已他的高拓展性取代了传统布局显示,同时配合协调布局,可以实现很多意想不到的酷炫交互,今天就和大家介绍一下,如何给RecycleView添加头布局和尾布局,同时你也可以通过自己的拓展实现更多复杂的布局. 首先我们先看一下效果: 实现头部尾部布局其实方法还是很多,这里我推荐使用Adapter设置itemType来做,那么问题来了,为什么这么做呢?因为拓展性更强,并且更加解耦.将逻辑都写在adapter中,比起写在activity中管理起来也更加方便. 首先,

MultiThread(VS2013 MFC多线程-含源码-含个人逐步实现文档)

原文:http://download.csdn.net/download/jobfind/9559162 MultiThread(VS2013 MFC多线程-含源码-含个人逐步实现文档).rar

【数据结构】用C语言实现顺序表的各种操作(包括头删,尾删,插入,逆序,摧毁,清空等等)

//顺序表的各种操作(包括头删,尾删,插入,逆序,摧毁,清空等等) //头文件 #ifndef _SEQLIST_H #define _SEQLIST_H #include<stdio.h> typedef int ElemType; #define INIT_SIZE 8 typedef struct SeqList { ElemType *base; size_t capacity; size_t size; }SeqList; int isempty(SeqList *list); in

【数据结构】用C++实现双链表的各种操作(包括头删,尾删,插入,逆序,摧毁,清空等等)

//[数据结构]用C++实现双链表的各种操作(包括头删,尾删,插入,逆序,摧毁,清空等等) //头文件 #ifndef _LIST_H #define _LIST_H #include<iostream> using namespace std; template<class Type> class DList; template<class Type> class ListNode { friend class DList<Type>; public: L

【数据结构】用C++实现双循环链表的各种操作(包括头删,尾删,插入,逆序,摧毁,清空等等)

//[数据结构]用C++实现单循环链表的各种操作(包括头删,尾删,插入,逆序,摧毁,清空等等) //头文件 #ifndef _CDLIST_H #define _CDLIST_H #include<iostream> using namespace std; template<class Type> class CDList; template<class Type> class ListNode { friend class CDList<Type>; p