有意思的魔方

魔方前段时间微软给技术部送来了几个魔方,办公室就开始流行玩这个东西。有个同事是玩魔方专家,我让他给捎了一个国丙的三阶魔方,花了40人民币,质量很不错,非一般地摊货可比。然后照着教程鼓捣鼓捣,基本也算可以弄全六面,当然都是照着教程一边看一边转的。

魔方的初级玩法一般按六步走:
1.拼好底面和底面的四个棱、四个角。这一步转转就弄会了,非常简单。
2.调整第二层的棱;
3.顶面划十字;
4.对好顶面;
5.调整顶面四角顺序;
6.排列好最后的棱色块。

每一步都有两种算法,但是其实所有的算法都是另一种算法的对角线镜像,转两遍的效果和另外一种是相同的,负负得正嘛:),所以不追求速度的话每一步记住一种算法就可以了。

教程地址:魔方的入门玩法
PDF下载:http://panweizeng.com/download/rubik-tutorial.pdf
为大家都能学好初级玩法,在这里特别免费放出秘笈图纸一张,打印出来每天演练一遍,十日可成。

更新(20080621):
我已经可以脱离教程和图纸完成六面。

[ 分类: 学习 Learning ] 由 Pan 发表于 June 10, 2008 12:16 am  固定链接  评论( 2 )

有意思的lambda表达式

有这么一种场景,有一个Category的实体类对应着一个名为Category的数据库表。Category类中有Name和Path属性,在新建或更新Category对象时要保证这两个属性在数据库中的值唯一。当然你可以在数据库中的Name和Path字段都建一个唯一索引,那如果不采用这种做法应该怎么做呢?最简单的方法就是写两个名为CheckNameValid和CheckPathValid的函数。很明显这种做法不易于维护,比如说当Category类增加其他需要保证唯一的属性或者属性名字需要改变的时候。我的写法如下:

  1. /// <summary>
  2. /// 检查数据库字段中的值是否唯一
  3. /// </summary>
  4. /// <param name="t">Cateroy的集合</param>
  5. /// <param name="func">表达式</param>
  6. /// <param name="isNew">是否是新建Category</param>
  7. /// <returns></returns>
  8. protected bool CheckValid(
  9.                 IEnumerable<Category> t,
  10.                 Func<Category, Boolean> func,
  11.                 bool isNew)
  12. {
  13.     Category c = t.SingleOrDefault(func);
  14.     if (isNew)
  15.         return c == null;
  16.     else
  17.         return c == null || (c != null && c.ID == Convert.ToInt32(ID.Text));
  18. }
  19.  
  20. //调用方法一
  21. protected Func<Category, Boolean> func = null;
  22. protected void Page_Load(object sender, EventArgs e)
  23. {
  24.     func = selectCategory;
  25. }
  26. protected bool selectCategory(Category c)
  27. {
  28.     return c.Name == this.Name.Text;
  29. }
  30. CheckValid(kr.Categories, selectCategory, true);
  31. //调用方法二
  32. CheckValid(kr.Categories, delegate(Category c){ 
  33.         return c.Name == Name.Text;
  34.     }
  35.     , true);
  36. CheckValid(kr.Categories, delegate(Category c){ 
  37.         return c.Path == Path.Text;
  38.     }
  39.     , false);
  40. //调用方法三
  41. CheckValid(kr.Categories, c => c.Name == Name.Text, true);
  42. CheckValid(kr.Categories, c => c.Path == Path.Text, false);

三种调用方法,当然是使用了lambda表达式的方法三最简洁明了。还可以扩展一下,可以把它写成某个Utility类泛型静态方法,专门检查实体类在数据库字段中的值是否唯一,这时Convert.ToInt32(ID.Text)就要抽出来做成一个参数,这里不就写了。

另外在瘦子师兄的点拨下,发现C# 3.0里面有个类似Javascript中prototype的概念,称为扩展方法(Extension Methods),在System.Linq中运用了大量的扩展方法(例如前面例子中的SingleOrDefault)。扩展方法貌似没prototype灵活,比较的例子如下:

  1. public static class Program 
  2. {
  3.     public static void Main(string [] args)
  4.     {
  5.         string s = "Hello World";
  6.         s.Print();
  7.         s.ToString();
  8.         Console.WriteLine(s.ToString());
  9.         Console.Read();
  10.     }
  11. }
  12. public static class Tools
  13. {
  14.     public static void Print(this string s)
  15.     {
  16.         Console.WriteLine(s.ToUpper());
  17.     }
  18.     public static void ToString(this string s)
  19.     {
  20.         Console.WriteLine(s.ToUpper());
  21.     }
  22. }
  1. function A(){this.name = "AA";}
  2. A.prototype = {
  3.     show:function(){ alert(this.name);}
  4. }
  5. var a1 = new A();
  6. a1.show();
  7. //直接覆写A的prototype中的show
  8. A.prototype.show = function(){ alert("modified:"+this.name);}
  9. var a2 = new A();
  10. a2.name = "a2";
  11. a2.show();

对于一个你不能修改源代码的类,可以通过这种方式为它添加方法。但这个只是编译期的(s.Print()翻译成tools.Print(s)),也就是说编译之后是不能通过反射来取得它的,它并不是要扩展类(例子中的String类)中的真正方法,而是某个静态类(例子中的Tools)的静态方法。
具体介绍参见这篇文章Deep Dive on Extension Methods

[ 分类: 学习 Learning ] 由 Pan 发表于 May 25, 2008 4:28 pm  固定链接  评论( 0 )

静态列表页的分页

相比于动态网站的列表页分页,静态列表页的分页是件麻烦的事情。因为旧的列表页发布出去以后就无法知道之后发布的总页数,而且也不可能每次更新列表页时去更新所有以前发布过的列表页(频道树节点多时,这个量非常大),所以一般的CMS处理得都比较简单。比如说新浪很多频道的列表页都是按日期归档的,一天发表的文章生成一个列表页,而有些专题的子频道列表页直接就没有处理分页。凤凰网列表页分页就只有上一页和下一页的链接,系统在处理的时候就更新两个列表页-当前最新的列表页和最后更新过的列表页,规则是当上次处理分页后更新的文章数大于等于三分之五Pagesize时进行分页操作。搜狐列表页就要复杂一些,有列表页首页链接,有输入页码跳转等,在页尾的Javascript脚本中里面有一个变量maxPage,猜测应该是后期include包含进去的。

在最近的一个项目中,对列表页的分页处理比上面说的几种要复杂一些。不看代码,先看效果
这里的静态列表页是这样的,最早发布出去的第一页是列表页名字_1.html, 第二页是列表页名字_2.html,以此类推,到最后一页就是最新的一页是列表页名字.html或者列表页名字_列表页总页数.html。

下面是代码

  1. var Paging = {
  2.     holder:'paging',
  3.     currentNo:1,
  4.     currentFileName:'',//当前文件名,不包括后缀名和页码
  5.     listSize:10,//列表项链接的显示数目
  6.     totalNo:35,//列表页总页数
  7.     //初始化currentFileName和currentNo
  8.     preRender:function(){
  9.         var _lastIndex = location.href.lastIndexOf('/');
  10.         var _fileName = location.href.substr(_lastIndex+1);
  11.         var matchItem = _fileName.match(/(.*)_(\d+).html/i);
  12.         if(matchItem != null){
  13.             this.currentFileName = matchItem[1];
  14.             this.currentNo = parseInt(matchItem[2]);
  15.         } else {
  16.             this.currentFileName = _fileName.split('.')[0];
  17.             this.currentNo = this.totalNo;
  18.         }
  19.     },
  20.     render:function(){
  21.         //在页面上显示的页码
  22.         var realNo = this.totalNo - this.currentNo;
  23.         //页码对listSize的倍数
  24.         var tens = Math.floor(realNo / this.listSize);
  25.         //页码对listSize的余数
  26.         var units =  realNo % this.listSize;
  27.         var html = '';
  28.         html += '<span><a href="'+this.currentFileName+'.html">首页</a></span>';
  29.         //如果不是在第一个listSize,例如第一个十页,就显示上一页
  30.         if(tens != 0) {
  31.             html += '<span>'
  32.                 +'<a href="'+this.currentFileName+'_'+(this.currentNo+1)
  33.                 +'.html">上一页</a></span>';
  34.         }
  35.        
  36.         var _no=0,_linkNo=0,_isCurrent=false;
  37.         for(var i = 1 ; i < this.listSize+1 ; i++){
  38.             _no = tens*this.listSize+i;
  39.             //链接为倒序
  40.             _linkNo = this.totalNo - _no + 1;
  41.             _isCurrent = i == units+1 ? true : false;
  42.  
  43.             //到达最后一页就退出
  44.             if(_linkNo < 1) break;
  45.             //是否当前页
  46.             if(_isCurrent)
  47.                 html += '<span class="current">';
  48.             else
  49.                 html += '<span>';
  50.             //是否是列表页第一页
  51.             if(_no != 1 && !_isCurrent)
  52.                 html += '<a href="'+this.currentFileName+'_'+_linkNo.toString()+'.html">';
  53.             else if (!_isCurrent)
  54.                 html += '<a href="'+this.currentFileName+'.html">';
  55.             html += _no.toString();
  56.             html += '</a>';
  57.             html += '</span>';
  58.         }
  59.        
  60.         //如果没到最后一个listSize,显示下一页
  61.         if(tens != Math.floor(this.totalNo / this.listSize)) {
  62.             html += '<span><a href="'+this.currentFileName+'_'
  63.                 +(this.currentNo-1)+'.html">下一页</a></span>';
  64.         }
  65.         html += '<span><a href="'+this.currentFileName+'_1.html">末页</a></span>';
  66.         $(this.holder).innerHTML = html;
  67.     },
  68.     init:function(){
  69.         this.preRender();
  70.         this.render();
  71.     }
  72. }
  73. Paging.init();

其中代码中的Paging.totalNo是列表页的总数,这个是后期加入的。目前可以想到两种方法给脚本传递这个值,一个就是像搜狐一样在页面中include进去一个后期更新的文件,另外还有比较别扭的方法,就是最后更新的列表页传入一个值,形如doclist.html?535,后续打开的页面都会带着这个值,如果万一不是从第一页进入的比如从搜索引擎过来的话,在页面中会记录一个该列表当时发布的总页数作为补充。最后还是决定使用搜狐的方法,在更新最新的列表页时同时新发一个记录列表页总数的文件。另外为了SEO,可以专门做一个汇总归档页,当然也可以由CMS生成上一页和下一页的链接,这些方法都是容易做到的。

脚本浏览地址:http://panweizeng.com/others/itv_playlist_paging/paging.js

[ 分类: 学习 Learning ] 由 Pan 发表于 May 24, 2008 10:12 pm  固定链接  评论( 0 )

在跑步机上跑一万米的心得

if(你.经常锻炼 == true || 你.意志力强 == true) return;

1.万米跑这种耐力运动最大的敌人是你的身体。
2.运动前的几个小时应该保证摄入充足的食物,饿着肚子跑是不现实的。这些食物应该含有足够的碳水化合物,米饭和馒头都是不错的选择。
3.穿着舒适的运动裤和上衣,要保证一双合脚的布鞋或者跑鞋,不好的鞋容易引起膝关节的损伤。
4.准备一个随身听(Mp3或磁带机均可),播放一些快节奏的曲目,分散自己的注意力。
5.运动前需要进行热身,快走和慢跑都可以,这个非常关键。热身使运动神经开始兴奋,身体开始进入运动状态,能有效防止运动的损伤和过度的疲劳感。
6.根据自己的身体调整运动的节奏。我一般是:10-20分钟快走(6km/h)热身,10分钟慢速跑(8km/h),10分钟快速跑(12km/h),10分钟快走,10分钟慢速跑,10分钟中速跑(10km/h),10分钟快走,10分钟慢速跑,最后10分钟快走结束。这样70-90分钟就能完成万米跑。
7.运动中及时补充身体的水分。纯净水和运动饮料都可以,但是注意不要暴饮,否则会加重胃部的负担。
8.如果很久都没有参加运动了,前面几次应该量力而为,不要勉强自己。因为长期不运动,心肺功能跟不上,大脑会容易缺氧,万一晕倒在跑步机上面就要受伤了。
9.如果跑步的目的和我一样,减脂不减肌肉,那么就要好好把握一下运动的时间。我之前的《减肥参考数据》文章中提到,我认为最佳运动时间应该在[1,1.5](单位:小时)之间。另外,即使你身上有很多的肥肉,但是运动也应该适量,毛主席教导我们:贵有恒,何必三更眠五更起;最无益,只怕一日暴十日寒。运动要持之以恒,偶尔突击一下肯定是不行的。

[ 分类: 学习 Learning ] 由 Pan 发表于 May 3, 2008 3:17 pm  固定链接  评论( 0 )

Flash版倒计时

原作品是06世界杯临近期间英文雅虎页面上的阿迪达斯倒计时牌冠名广告。设计富有金属质感,属于我比较喜欢的风格。反编译以后发现代码比较乱,然后手就有点痒,再然后就有了下面的代码。
首先是CountDown的类,其中handler是Function类型的公有变量,该函数将用于处理怎么显示倒计时代码。

  1. class CountDown {
  2.     private var finalTime:Date;
  3.     private var currentTime:Date;
  4.     private var timeDiff:Number;
  5.     public var handler:Function = null;
  6.    
  7.     public function CountDown(date:Date) { 
  8.         this.finalTime = date != null ? date : new Date(1900, 0, 0, 0, 0, 0);
  9.     }
  10.    
  11.     private function init():Void {
  12.         this.currentTime = new Date();
  13.         this.timeDiff = finalTime.getTime() - currentTime.getTime();
  14.         if (this.timeDiff < 0)
  15.             this.timeDiff = 0;   
  16.     }
  17.    
  18.     public function run():Void {
  19.         init();
  20.         var _day:Number = Math.floor(timeDiff/(1000*60*60*24));
  21.         var _hour:Number = Math.floor((timeDiff/(1000*60*60))%24);
  22.         var _minute:Number = Math.floor((timeDiff/(1000*60))%60);
  23.         var _second:Number = Math.floor((timeDiff/1000)%60);
  24.         show(_day,_hour,_minute,_second);
  25.     }
  26.    
  27.     private function show(day:Number, hour:Number, minute:Number, second:Number):Void {
  28.         if(handler != null)
  29.             handler(day,hour,minute,second);
  30.     }
  31. }

在倒计时影片剪辑中加入以下代码,其中1和2两个位置的判断很重要。

  1. var cd = new CountDown(new Date(2012, 8, 22, 0, 0, 0));
  2. var oldDigitString = "000000000";
  3. function doStart(day,hour,minute,second){
  4.     doShow(digitFix(day,4)+digitFix(hour,2)+digitFix(minute,2)+digitFix(second,2));
  5. }
  6.  
  7. function doShow(digitString){
  8.     if(oldDigitString == digitString) return;//1.
  9.     for(var i = 0; i < digitString.length; i++){
  10.         if(oldDigitString.charAt(i) != digitString.charAt(i))//2.
  11.             doDisplay(eval('d_'+i),oldDigitString.charAt(i),digitString.charAt(i));
  12.     }
  13.     oldDigitString = digitString;
  14. }
  15.  
  16. function doDisplay(o,oldNum,newNum){
  17.     o._prev = oldNum;
  18.     o._next = newNum;
  19.     o.gotoAndPlay(2);
  20. }
  21.  
  22. function digitFix(num,len):String{
  23.     while(num.toString().length < len)
  24.             num = '0'+num;
  25.     return num.toString();
  26. }
  27.  
  28. cd.handler = doStart;
  29.  
  30. this.onEnterFrame = function()
  31. {
  32.     cd.run();
  33. }

剽窃后的成果-_-!

对不起,您的浏览器禁用脚本或者您的Flash播放器版本较低!请点击这里获取最新版本。

其中这里的终点时间是我30岁的生日,到倒计时牌都是0000000000的时候,不知道我会不会哭泣……

[ 分类: 学习 Learning ] 由 Pan 发表于 3:05 pm  固定链接  评论( 0 )