最近做的项目(自己练练手的那种)遇到了一个很有趣的问题,比如这样一个字符串,http://www.example.com/{field1}/{field2}/{field3},当然这里的field个数是不确定的,但是每一个field都知道其范围,要实现的功能就是把所有的url计算出来。

  如果field的个数确定,你大可放心地用for循环,不断地嵌套就可以实现,但是这里是不确定的,我想到的方案是使用Stack,用Stack来模拟嵌套的for循环。

  首先为field建个模型,说白了就是写了class表示它:

 

    public class RangeItem
    {
        private int position = 0;
        /// <summary>
        /// 对应的field位置
        /// </summary>
        public int Position
        {
            get { return position; }
            set { position = value; }
        }

        private int rangeMin = 0;
        /// <summary>
        /// 范围最小值
        /// </summary>
        public int RangeMin
        {
            get { return rangeMin; }
            set { rangeMin = value; }
        }

        private int rangeMax = 0;
        /// <summary>
        /// 范围最大值
        /// </summary>
        public int RangeMax
        {
            get { return rangeMax; }
            set { rangeMax = value; }
        }

        private int currentIndex = -1;
        /// <summary>
        /// 在Range中的位置,这个属性用于在循环中保存当前的处理序号,因为我们在用Stack模拟嵌套的for循环
        /// </summary>
        public int CurrentIndex
        {
            get { return currentIndex == -1 ? rangeMin : currentIndex; }
            set { currentIndex = value; }
        }
    }

  然后呢再封装一下,把所有的RangeItem集中管理

 

    public class RangeItemManager
    {
        private static List<RangeItem> rangeItems = new List<RangeItem>();
        /// <summary>
        /// 所有的RangeItem
        /// </summary>
        public static List<RangeItem> Items
        {
            get { return RangeItemManager.rangeItems; }
            set { RangeItemManager.rangeItems = value; }
        }
    }

  核心的代码如下

 

	if (RangeItemManager.Items.Count == 0)
		return null;
  
	RangeItemManager.Items.Sort(Compare);

	RangeItem firstItem = RangeItemManager.Items[0];
    //如果只有一个field,则简单地通过一个for处理
	if (RangeItemManager.Items.Count == 1) {
		for (int i = firstItem.RangeMin; i <= firstItem.RangeMax; i++)
			Action(i);
	} else {
      //循环最外层的,对应field1
		for (int i = firstItem.RangeMin; i <= firstItem.RangeMax; i++) {
			stack = new Stack<int>();

			//把最外层的那个值入栈
			stack.Push(i);
           //循环剩余的RangeItem
			ProcessInnerLoop(0);
		}
	}
	   
	public static int Compare(RangeItem x, RangeItem y)
	{
		return x.Position > y.Position ? 0 : 1;
	}

	private static void ProcessInnerLoop(int current)
	{
		++current;

		//如果是最里层(fieldmax),则直接进行遍历
		if (current == RangeItemManager.Items.Count - 1) {
			RangeItem item = RangeItemManager.Items[current];

			for (int i = item.RangeMin; i <= item.RangeMax; i++) {

				//获取全部入栈的元素,这样算是完成了一次处理,可以产生了一个url
				List<int> lst = stack.ToList();

				lst.Reverse();
				lst.Add(i);

				Action(lst);
			}
		} else {
			RangeItem item = RangeItemManager.Items[current];

			for (int i = item.CurrentIndex; i <= item.RangeMax; i++) {

				//把当前值入栈
				stack.Push(i);

				//处理下一个RangeItem.
				ProcessInnerLoop(current);

				//修改当前RangeItem的CurrentIndex,下一次循环是从CurrentIndex开始的
				item.CurrentIndex = i + 1;

				if (item.CurrentIndex >= item.RangeMax)
					item.CurrentIndex = item.RangeMin;

				//把当前值出栈
				stack.Pop();
			}
		}
	}

	private static void Action(List<int> lst)
	{
		object[] objs = new object[lst.Count];
		for (int i = 0; i < lst.Count; i++) {
			objs[i] = (object)lst[i];
		}

		string url = String.Format(UrlFormator, objs);
       //TargetUrls.Urls是一个List<string>
		TargetUrls.Urls.Add(url);
	}

	private static void Action(int i)
	{
		string url = String.Format(UrlFormator, i);
      //TargetUrls.Urls是一个List<string>
		TargetUrls.Urls.Add(url);
	}

 

  贴了这么一大段代码,给我的感觉就是啰嗦,效率也是个问题,尤其是在field很多、每个RangeItem范围很广的时候,写这篇post的目的就是大家一起讨论,看看有没有更好的实现方式,谢谢。

posted @ 2010-05-26 21:50 dongzz 阅读(98) 评论(0) 编辑

  如果你经常学习新技术,那不可避免会写很多demo,时间一长你VS文件菜单下的"Recent Projects"肯定乱作一团,就像我的这样:

  

  清理的办法也很简单,最直接的方法就是使用最下面的"Clear Recent Project List",但是如果你想调整一下两个Item的顺序,我提供的这个工具就可以帮上忙了。

使用方法很简单,选择你当前的VS版本,点一下"load"按钮,然后通过右边的按钮进行调整、删除,完成后点"save"就行了,重启IDE生效。

最后附上下载地址 Download,由于个人水平有限,难免有bug,希望大家在评论中指出。

posted @ 2010-05-26 17:21 dongzz 阅读(37) 评论(0) 编辑