现在的位置: 首页 > 综合 > 正文

C++ primer中的for循环写法、数组轮转、取模操作

2017年12月21日 ⁄ 综合 ⁄ 共 2244字 ⁄ 字号 评论关闭

之前看C++ primer,对于for循环,书上说C程序员习惯写

for(i=0;i<sentinel;i++)

而C++程序员习惯写

for(i=0;i!=sentinel;i++)

当时感觉明显是 i<sentinel  更好,至少说没发觉写不等号有什么优势,而且 i<sentinel 更好理解。

然后最近遇到这么个事情:

做一个游戏的轮盘选人界面,就是一排人站个圆圈,有前有后(zOrder),拖动一下,一个旋转动画,后面的人就到前面来了,前面的人就到后面去了。类似下图:

我是这么想的,每个人物是一个节点,用数组存储(或许用循环链表更方便),定义一个操作,如

void moves()
{
//左移一个或者右移一个
}


规定逆时针为前面,最前面的人物为数组0,左移一个,就是把当前所有人物取他 i+1 人物的坐标,右移就是把当前所有人物取他 i-1 的左边,然后就重复调用这个函数,就可以了。

于是我们把这个问题简化如下:

现在有一个长度为12的数组 int nodes[12] ,要求把数组向前或者向后移动1位。比如 12 3 4 前移一个是 4 1 2 3 ,后移一个是 2 3 4 1。

但是按照之前上面说的有2个问题。

1、假设是前移,要把, 那么就应该让 nodes[0]的值换成 nodes[1]的值,nodes[1]的值换成nodes[2]的值。假设是后移,就应该把 nodes[0] 的值换成 nodes[11] 的值,nodes[11]的值换成nodes[10]的值。

    问题就在于,这2个操作的操作顺序是反的,如果按照正常for循环的写法

for(i=0;i<12;i++)


    那么他对数组的操作顺序都是从 0到11,但是后移的时候,如果按照这个顺序操作,拆分下来就是这样

nodes[0]=nodes[11];
nodes[1]=nodes[0];
nodes[2]=nodes[1];
... ...


    因为一开始第一步,就让nodes[0]=nodes[11],后面的操作其实都是在给所有节点赋值nodes[11]。为了让他正常工作,就需要让他是11,10,9...这么个顺序。那么一个for循环就搞不定了。就需要写另外一个是反方向的。

2、数组12个元素,总共12个空间,需要用一个tmp空间来放其中一个元素,才能让轮转操作顺利进行。

    但是,假设是前移,一开始tmp=nodes[0],他的最后一个元素就应该是 nodes[11],但是假设是后期,最后一个就成了 nodes[1],也是个矛盾的地方。

所以,看上去貌似最好的办法是分成 moveForward 和 moveBackward 两个函数来做。但是如果这么的话,其实2个函数只有1、2句不一样,其他都要大量复制。

由于系统原因,拖动向右,会记为正数,向左会记为负数。然后我就想当了其实可以用取模操作来做这个事情。就可以利用正负号来标记移动方向,省去一个判断。但是这样还是不能规避问题1,就是数组index的方向问题。

然后我想到了下面的方法。用一个 step 来表示 移动方向,这个值是 1 或者 -1 

for(i=0;i<sentinel*step;i+=step)  //错误写法,下面说


上面这个是雏形,但是,如果是正数,那么是 <sentinel*step 没错,但是如果是负数,就应该是 >sentinel*step 才对。这是问题一。

问题二,数组的下标是不能为负的。所以这样看起来貌似还是不能放在一个for循环中执行。

然后就要祭出神器了:

还记得C++ primer的写法么

int tmp=nodes[0];
for(int j=0;j!=sentinel*step;j+=step)
{

	if(current==last)
	{
		nodes[j%mod]=tmp;
	}
		else
	{
		nodes[j%mod]=nodes[(j+1)%mod];
	}
}


这样一来,最开始提到的2个问题就都解决了,这2个就可以放到1个for循环中处理了。

但是其实上面这个式子还有一个错误,就是对于 取模操作的问题。

之前我一直记得 -1%12=11,所以写出了这个式子,事实证明我错了, -1%12 = -1 ,那么貌似在取模的结果加一个mod就可以解决这个问题。但是如果是正数呢;那么在取模之前加一个mod貌似可以解决,但是如果这个负数加了mod都还不是正数呢。那么我们就对取模的结果加以判断,看是正事负,但是别忘了之所以这么麻烦就是想放到一个for循环中解决这种问题,并且不写if,让他有通用性。

所以有了下面这个

#define MOD(x,y)  (((x%y)+y)%y)

然后整个就成了这样

	int sentinel=sentinel*step;                    
	int last=MOD(11*step,mod);

	int tmp=nodes[0];

	for(int j=0;j!=sentinel;j+=step)
	{
		int current=MOD(j,mod);
		int next=MOD((j+step),mod);

		if(current==last)
		{
			nodes[current]=tmp;
		}
		else
		{
			nodes[current]=nodes[next];
		}
	}


之所以把 sentinel 和 last 放在外面,把 current 和 next 也单独算出来,是为了避免直接写 i+1 这种,每次都要算带来的效率开销。

但是不得不说虽然我把这个操作合成了一个操作,但是效率貌似还不如一开始直接分成moveForward和moveBackward来的高(未验证)。

但是我就是这么偏执,就想把他写成一个for看着舒服。

然后再看看能不能有什么优化的。。。。。

抱歉!评论已关闭.