Archive

Author Archive

我和JavaScript的那些事儿 —— 记Web标准化交流会2010.8

August 30th, 2010

这一期交流会在腾迅soso举行。本期交流会的主题是我建议裕波的,叫做“我和JavaScript的那些事儿”,主要是希望借着交流会交流为主的特色,让大家谈谈在学习JavaScript过程中的经历和自己对使用和认识JavaScript的一些心得。

我自己是05年才开始接触JavaScript的,在这之前,接触前端的东西不多,虽然也写过perl和php的小网站,但原先我更多是做做单片机以及一些桌面软件的,所以用C语言、C++和C#比较多。对JavaScript,最初的认识我和一些同学一样,认为它是一种简单的不完善的脚本语言,不值得深入研究。而当时早期的互联网前端确实也对JavaScript研究不多,大部分网上的介绍和书籍都把JavaScript描述为在页面上增加一些特效的辅助脚本,这加深了人们对JavaScript的误解。04年毕业后到了深圳去之后,因为工作的关系,用Java。当时我们做信息系统,是web的,需要写一些前端的东西,当时团队里面没有做前端的人,所以我只好自己去研究,所以从05年开始我自己慢慢接触了JavaScript。当时主要是通过看书和在51js上和别人交流来学习JavaScript的。我搜了一下我在51js上发表的文章,最早的是2005年4月22日的,差不多我也就是从那时候开始真正接触的前端开发。

我对脚本的认识和研究一开始是从别的语言入手的。客观上因为当时我所在的团队没有专业的前端工程师,大家对Java和一些后端语言比较熟悉,对JavaScript没有什么认识,所以为了让大家能够顺利使用这种脚本语言,我当时的思路是去研究如何用JavaScript去模拟别的语言的特性。因此我在51js上发表的第一篇文章是对javascript重载的深入探讨,这是一种语言对另一种语言特性的模拟。从一开始,我就是专门从语言特性入手对JavaScript进行学习和研究的,这对我以前的经历来说是最佳的学习方式,但是,在很长一段时间内这仅仅是针对语言的研究,甚至不能算是前端的东西,JavaScript在这里只是一种特定的语言工具而已,而我研究的目的,仅仅是为了让不懂JavaScript的同事能够很方便地上手使用这门语言。由于信息系统的浏览器兼容性要求不高,因此对浏览器的研究,是在我经历了很长一段时间的实践之后才慢慢开始的。直到现在为止,浏览器兼容这一块也还是我的弱项。

要用一种语言很好地模拟另一种语言,必须对它的语言特性非常了解,随着我对脚本特性研究的深入,我发现我喜欢上了这种以原型(Prototype)为基础的语言,相对于类继承来说,原型在轻巧和严谨之间保持了一种微妙的平衡,我称之为“优雅”。对JavaScript的了解越深入,我对它的特性越喜欢,我发现它的模拟能力非常强大,不但能模拟类继承的面向对象语言,也能模拟出其他你想要的语法特性,例如这一篇我在51js上发表的文章——令人惊讶的用法…用Js模拟C#的Attribute ^^。我将对语言特性的模拟研究得比较深之后,将想要的特性集中起来,慢慢形成一个脚本框架,即后来的Silvern的第一版。这个框架的类库主要是模拟了别的语言的特点,实现了面向对象继承,并且用一个基本模式将数据和页面过程规范起来,让开发者能够以他们所熟悉的后端语言类似的方式来完成开发。这个框架当时是我独立构思的,除了后期参考了jsvm的一些思路之外,没有看过别的框架。而这个框架同现在业内成熟的一些脚本框架来比较,出发点也是很不同的,它主要的目的依然是让后端人员用他们熟悉的方式来写前端代码,因此不是一个现在意义上完整的前端框架,除了必要的Ajax封装之外,它对于浏览器差异等等纯前端的问题几乎都没有处理。后来的一段时间内我在实践中完善这个框架,在后续版本中增加了“元类”这个概念,让它变得更加动态。而正是因为研究“元类”我对闭包这类概念有了比较深入的认识,也开始研究functional。

Silverna第一版大概到06年中的时候停止开发,第一个原因是它已基本满足当时的开发需求,第二个原因是随着我慢慢对前端开发有了更加深入的了解后,我基本放弃了用JavaScript模拟后端的想法,开始寻求真正的前端开发解决之道。可以说到那时候起,我才算真正入了前端的门。

此时此刻,我回想起JavaScript的学习历程,几年前的一些记忆还很清晰,可以说通过学习JavaScript这门优秀的语言我得到了很多东西,也可以说是JavaScript引导我进入前端开发这个行业的。感谢JavaScript,感谢前端开发,带给我快乐,也让我有今天的成就。

———-以上是一些个人感慨,我是分割线———-

在交流会上除了交流对JavaScript学习的经历之外,还有不少牛人给我们做了精彩的分享。感谢曾哥、感谢阿当、感谢rank、感谢因为迟到我没有听到分享的吕婷美女:P。

感谢腾迅soso送给我们的QQ仔,我居然带了两大两小4只回家 =.= ,其中有一只小的应该是拿错了坐在附近的别的同学的,希望不要责怪我,汗。。。。。。

不知不觉,web标准化交流会已经快要迎来一岁生日。感谢裕波和组办者们为了交流会投入的时间和精力,也感谢所有为交流会提供场地的公司。在参加的每一期交流会里,我都能收获很多,希望交流会越办越好。

月影 Web标准化交流会, 前端技术

JavaScript 通过模式匹配实现“重载”

August 11th, 2010

昨天rank同学向我提出一个问题,在实际应用中有些接口需要提供类似于函数重载的功能,以方便开发者组织代码逻辑,简化使用者调用。

正好infinite同学提出“更优雅的兼容”其实也和这个问题有一定的关联(我们后面会看到)

在youa的脚本库中Function的Helper中,添加支持重载的模式匹配

阅读完整文章请猛击这里

月影 Uncategorized

关于selector选择符解析的方法

July 14th, 2010

先推荐关于js selector的好文章
http://www.never-online.net/blog/article.asp?id=295
http://www.never-online.net/blog/article.asp?id=296

这里只讨论没有特殊位置关系的情况,第一种方案是常规的从左往右查
例如对于 div p
采用普通的查找过程如下:

1.先查找页面上所有的div
2.循环所有的div,查找每个div下的p
3.合并结果

由于dom节点的数据结构是一棵树,对于这种查找方法,算法的时间复杂度为O(N^2),N为节点总数

另一种方案是从右往左查,这也是一些浏览器引擎采用的方法

这种方法的查找过程如下:

1.先查找页面上所有的p
2.循环所有的p,查找每个p的父元素
 1.如果不是div,遍历上一层。
 2.如果已经是顶层,排除此p。
 3.如果是div,则保存此p元素。

可以证明,这种方案的算法时间复杂度降低到O(N*logN)

但是有没有别的的方法呢?
答案是有——

还是按照从左到右的方法查,但是,先对 div div进行一次除重,即只保留最外层的div
理由是,命题div div a => div a为真

所以:

1.先找出所有的div元素,对这些div元素进行遍历,删掉那些祖先元素为div的div

这个算法看起来是O(N^2)的,但是实际上不是,因为——

document.getElementsByTagName(’div’) 获得的节点不是完全无序的,而一定是祖先在前子孙在后,并且是深度优先遍历的
所以有如下定理:
集合[div1, div2, div3... div(n)] 是 document.getElementsByTagName(’div’)的查询结果
若 div(k) 不是div1的子孙,则div(k+1) div(k+2)… div(n) 都不是div(k)的子孙

所以,除重算法如下:

  1. var divs = document.documentElement.getElementsByTagName('div');
  2. divs = makeArray(divs);
  3.  
  4. for (var i=1; i<divs.length; i++) {
  5.   if (dom_contains(divs[i-1], divs[i])) {
  6.   //除重,复杂度N。
  7.   //如果dom里前者包含后者,则移者后者
  8.   //当前循环标记-1
  9.   //结果将是
  10.   //第一轮:[1,3,4,5,6], i=1
  11.   //第二轮:[1,4,5,6] i=1
  12.   //第三轮:[1,5,6] i=1
  13.   //第四轮:[1,6] i=1
  14.   //第五轮:[1] i=1
  15.     divs.splice(i,1);
  16.     i--;
  17.   }
  18. }

可以看出这是一个O(N)复杂度的除重算法
进行完这个除重之后,只需要将除重后的div的子孙p合并起来即可
这样整个算法就简化为O(N),成为一种更高效率的方法

月影 Uncategorized

Church计数和Lambda演算——不用“数”的自然数运算

July 6th, 2010

参考:http://shellex.info/church-nurmal-and-lambda-calculus/

  1. var zero = function(f){
  2.     return function(x){return x};
  3. }
  4.  
  5. var one = function(f){
  6.     return function(x){return f(x)};
  7. }
  8.  
  9. var two = function(f){
  10.     return function(x){return f(f(x))};
  11. }
  12.  
  13. var add = function(n, m){
  14.     return function(f){
  15.         return function(x){
  16.             return n(f)(m(f)(x));
  17.         }
  18.     }
  19. }

如果要翻译回普通“自然数”,可以用下面的

  1. var inc = function(x){
  2.     return x?++x:1;
  3. }
  4. alert(add(two,add(two,one))(inc)());

这个加法是没有用迭代和效率问题的(当然“翻译”的时候需要),另外把inc的++改成 –,就翻译成“减法”了

月影 Uncategorized

关于知识积累与知识管理——谈第九期Web标准化交流会

June 28th, 2010

  不知不觉,已经到第九期了,Web标准化交流会已经走过了十个月,很快即将迎来她的一岁生日吧,在这里还是要先感谢国家,然后感谢为标准化交流会默默付出的组织者们。
  知识管理,是一个我思考了很久的问题,其实这个问题包括两个层面,一个是自我的知识收集和整理,另一个是团队的知识管理和发布,联系这两个层面之间的那个词,叫做“分享”。
  自我知识整理和收集的目的是为了个人能力的提升,实际上学习的过程本身也就是知识在大脑中整理的过程,所以从广义上来说,一个人从出生开始,就在对自我的知识进行着管理。Web前端是一个快速发展,知识更新速度非常快的领域,它的这个特点让个人的自我知识管理变得重要,在这里,除了普通的学习、理解和记忆之外,合适的工具和合适的手段能够更好地帮助前端开发者获得知识和提升能力。每个人的方法是不一样的,有些人善于使用搜索引擎,有些人很好地利用GReader,也有些人借助于evernote和onenote等工具,不管怎么样,对于个人来说,适合自己的方法是最好的方法,寻找对自己来说最高效率的工具,帮助自己养成习惯,是快速提高自身能力的好办法。
  讨论过程中大家还是在自我知识整理和收集这一块谈得比较多,有人说依靠工具订阅,有人说依靠搜索引擎,也有人说依靠大脑的记忆就够了。这一块我个人觉得适合自己的就是最好的,但是不能因此放弃提高要求,拒绝挖掘自己的潜力。
  对团队来说,知识管理和团队成长息息相关。几年前在学校里带着一支技术团队的时候,就已经思考过积累与发展的问题,一个团队要成长,绝对不能每年重复前一年做的事情,要不断提高层次,而提高层次的方法,除了每个组员的经验积累之外,平台的知识积累和分享也一定会起到重要的作用。对于团队的知识管理来说,很重要的一个关键词在于”执行力“,只有推动执行,帮助大家养成习惯,一个团队才能形成收集整理发布和分享知识再继续整理发布的良性氛围,从而让知识平台成为团队成长的助推器。
  
  

月影 Uncategorized

忍受痛苦与坚守快乐

June 27th, 2010

阅读蔡同学的分手的时刻,很有感触
http://jerrylovesrebol.blogspot.com/2010/06/blog-post_24.html

  记得在交流会上,曾经不止一次讨论起这种问题:什么是专业的前端开发工程师?我认为具有专业精神的任何职业除了必须精通该职业需要的基本技能之外,首要的一点是必须很清楚地知道这个职业要做什么,以及自己想做什么。真正优秀的人,在规划他或她的职业发展道路的时候,永远是努力把自己想做的事情和职业目标相匹配,让追求职业的过程变成追求满足与快乐的过程。
  身边有很多人,他们对技术的追求和热爱超过我,他们是天生的专家,是值得学习和尊敬的榜样,也有另一些人,他们最终发现自己并不适合软件工程师这个职位,所以选择了转行,其中也不乏有成功者。真正碌碌无为的那些人,要么是痛苦忍受,一点也不喜欢软件开发这个行业,要么是安于现状,得过且过,这两种人,向环境屈服,被环境改造,失去了自己的目标,所以也就远离的成功之路。
  有的人以适应艰难的环境为荣,但事实上我的观点是,只有让自己快乐的环境才是最有可能让自己成功的环境,人在职场,对外界压力要有足够的韧性,但是对周围环境,不能太逆来顺受,该有的原则一定要有,如果一个环境,不适合自己,果断放弃,重新选择,对自己才是最好的。
  迎接挑战的时候,一定要问自己内心是否快乐,自己内心的快乐,才是最适合自己发展的状态,真正的成功者,在克服别人看来很痛苦的困难时,实际上他或她的内心深处,依然坚守一份快乐。
  造就成功的,不是忍受痛苦,而是坚守快乐。

月影 Uncategorized

模拟摇号测试

June 4th, 2010

for 刘洪波:“五连号”不是数学问题

  1. function init(n,m){
  2.     var ret = new Array(m);
  3.     var i = 0;
  4.     while(i < n){
  5.         var r = Math.floor(Math.random() * m);
  6.         if(ret[r]) continue;
  7.         ret[r] = ++i;
  8.     }
  9.     return ret;
  10. }
  11.  
  12. //alert(init(218, 786));
  13.  
  14. function testK(arr,k){
  15.     var t = 0;
  16.     for(var i = 0; i < arr.length; i++){
  17.         if(!arr[i]) t=0;
  18.         else t++;
  19.         if(t == k) return true;
  20.     }
  21.     return false;
  22. }
  23.  
  24. function test(k, n){
  25.     var r = 0;
  26.     for(var i = 0; i < n; i++){
  27.         if(testK(init(208, 786), k)) r++;
  28.     }
  29.     return [n,k,r];
  30. }
  31. var r = test(5,1000);
  32. document.write(["测试次数"+r[0],"连号长度>="+r[1],"出现连号数"+r[2]]+"<br/>")
  33. var r = test(6,1000);
  34. document.write(["测试次数"+r[0],"连号长度>="+r[1],"出现连号数"+r[2]]+"<br/>")
  35. var r = test(7,1000);
  36. document.write(["测试次数"+r[0],"连号长度>="+r[1],"出现连号数"+r[2]]+"<br/>")
  37. var r = test(8,1000);
  38. document.write(["测试次数"+r[0],"连号长度>="+r[1],"出现连号数"+r[2]]+"<br/>")
  39. var r = test(9,1000);
  40. document.write(["测试次数"+r[0],"连号长度>="+r[1],"出现连号数"+r[2]]+"<br/>")

月影 Uncategorized

漫谈抽象本质 —— 从框架与库设计实战展开

April 25th, 2010

  四月份的Web标准化交流会昨天在北航举行,两位嘉宾给我们奉献了精彩的演讲。Giorgio关于IE9的精彩演讲给我们展望了web标准的未来以及Microsoft在这方面的决心,而爱民兄关于架构实战的演讲更让我收获良多。下面我就想接着这个话题展开谈谈我的一些体会。
  在演讲中,爱民通过SSDownloader的例子来展示了一个框架的分析、设计到最终产出的基本过程,解释了在最终产出的结果中什么层面上属于框架、什么层面上属于库,什么层面上属于具体应用设计。这个分解比较精妙,也很容易理解,实际上一个框架的最终产出是一个完整的抽象过程,框架代表规则的限定范围,库代表构成规则基本元素的部件集合,而应用则代表具体规则下实现某类需求的具体过程。一个成熟的框架以一个稳定的规则去解决一类能解决的问题,而解决的过程则交给应用设计去通过库来实现,更关键的是,框架能让一类问题在被解决前可解决性就已经得到验证,因为框架的存在本身就是规则的存在,它具备了验证完备性的可能。这里拿房子做一个比喻,一个人购买一套商品房的时候不用等到装修完毕再去验证房子住不住得了人,因为“居住”这个基问题是框架得以解决和验证的问题,装修决定了怎么住舒服的问题,但不影响基本框架,当你入住房子时,你可以改变装修,但是不用把框架推倒重来。事先验证可用性能把产出风险降低,而这就是框架的价值体现以及架构师的作用之一。
  从根本上讲,框架的实现是抽象的过程,在演讲中爱民提到抽象有共性抽象和本质抽象两种,但实际上我认为在现实生活中,这两种抽象的界限并没有那么清晰。什么才是本质,这个是没有定论的,从古到今,哲学家和自然科学家们都在寻找“本质”,然而至今还没有人能很有力的回答“本质”是什么。而数学家是善于抽象的一群人,他们更多地寻求共性而非本质,也许共性抽取的越多,就接近于“本质”,但谁知道呢,又有谁真正理解本质的涵义。道格拉斯的科幻小说《银河系漫游指南》中说到关于生命、宇宙的终极问题的答案是42——可没人知道42究竟意味着什么。所以,我认为现阶段抽象更多的是指共性抽象,本质抽象可能仅指接近某个状态,那个状态是什么,我不知道。
  数学的符号抽象和计算机语言有异曲同工之妙,在数学领域,简单的加、减、乘、除、乘方、开方,是由符号表示的过程抽象,有理数、实数、向量、矩阵则是数据抽象,它们之间的预算则是规则的演变,并不耦合特定的数和符号,所以从这个意义上说,一组特定符号就能构成一个完美的框架。记得还在大学的时候,学信息论的时候,我自己把的定义从实数扩展到向量和矩阵,结果发现整个课本上从第一章到最后一章的所有习题几乎都能用两个公式来完美表达,现在想来这就是一个框架的魅力。在51js上曾经跟人争吵过一个问题,其实那个问题背后的原因就是在一个设计上,我把“mul”作为一个抽象符号,而对方则作为一个函数标识,所以最终导致不同的实现。这个问题本身没有绝对的对和错,两个设计者站在不同的层次和角度上看问题而已。
  抽象本身是一个过程,数学符号或者语言表达是实现抽象的工具,计算机语言是一种非常受限的语言,然而只要这个语言是“图灵完全“的,他们在最根本的表达能力上都是没有区别的,比计算机语言更复杂的自然语言是显然能容纳图灵机的,因此不论是中国人、英国人、还是法国人、意大利人,大家在最基本的表达能力上是没有什么区别的,但是语言特性会影响思维习惯,不过这不是我想谈论的重点。
  最后回到问题的出发点,再次强调最初的问题,不管框架还是库,软件设计本身就是一个抽象的过程,框架和库是抽象的具体产出物,用抽象思维表达问题必然会导向这样的结果,因此架构师要做的事情是寻找规律和表达问题,在这里经验很重要,另外就是要避免陷入演算细节。
  前面说了那么多,其实只谈了实践层面上的设计抽象问题,这只是架构师工作的一小部分内容,关于系统架构师的工作范围,我同意爱民的观点,我觉得要成为一名优秀的系统架构师,我的路还很遥远……

月影 Uncategorized

浅谈数据抽象、过程抽象与泛型(一)

April 6th, 2010

  现代计算机语言很关注问题的数据层面,高级语言提供了很强大的数据抽象能力。例如面向过程的语言提供了对数据操作流程的抽象,而面向对象的语言则更进一步提供了构造对象、封装以及继承等能力。对于JavaScript而言,原型机制也是一种相当灵活的抽象数据的工具。
  如果说数据抽象是与现代程序设计发展相匹配的语言能力的扩充,那么对过程的抽象或许是被现代程序设计所忽略的一种能力。范型成为一种模式,而不是一个根本的语言核心上的本质特征,至少主流语言从语法层面上对过程抽象的支持很少,这一点不得不说是一种遗憾。
  我们说数学是定义问题和赋予符号,而计算是描述过程,过程抽象既是将过程用符号来表示,它是将数学和计算统一起来的工具。
  举一个简单的例子,我们说乘方“pow”是一个过程,事实上它可以用一组乘法“mul”过程来描述。在数学表达层面上,我们并不关心pow和mul的数据对象是什么类型,而这在计算机语言中,通常被弱化为某种基本数据类型(例如浮点数)的基本运算操作。
  在JavaScript中,Math.pow函数提供了计算浮点数乘方的功能,但是如果我们需要计算向量或矩阵的乘方,那么就需要另外去实现一个pow方法,或者说,Math.pow在过程上的抽象程度是很低的。
  在表达数学问题上,我们可能会用一种超越数据类型的符号表示方法,比如我们定义乘方这个操作——

  1. define D pow E ->
  2.     E < 0 and normalize D div (D pow -E),
  3.     E = 0 and normalize D,
  4.     E > 0 and
  5.         E rem 2 and D mul (D pow (E-1)),
  6.         or (D mul D) pow (E div 2).

  这种描述一气呵成,我们在这个问题领域内并没有深究and、 div、rem、 mul、 normalize等计算的实现,而仅仅是用符号来描述。当然并不影响过程定义本身的完备性,只是,这些符号还不能被解释成具体的可操作的步骤。下面我们来规定它——

  1. defind A and B ->
  2.     A && B.
  3. define A div B ->
  4.     (A - (A rem B)) / B.
  5. define A rem B ->
  6.     A % B.
  7. define A mul B ->
  8.     A * B.
  9. define normalize A ->
  10.     1.

  好,现在我们分别赋予了计算符号本身的意思,但是说到这里也没有什么特别之处,因为我们只是描述了过程而没有去描述数据模型的结构。例如普通来说,A mul B等同于A * B,这几乎是废话,但是如果A、B是二阶矩阵的时候,一切就有了变化:

  1. define [[X11,X12],[X21,X22]] mul [[Y11,Y12],[Y21,Y22]] ->
  2.          [[X11 * Y11 + X12 * Y21, X11 * Y12 + X12 * Y22],
  3.           [X21 * Y11 + X22 * Y21, X21 * Y12 + X22 * Y22]].
  4. define normalize [[X11,X12],[X21,X22]] ->
  5.         [[1,0],[0,1]].

  在这里我特别定义了一个二阶矩阵的mul和normalize方法,一个二阶矩阵是一个具有形如[[X11,X12],[X21,X22]]结构的二维数组。这个定义完成之后,你会发现pow对于矩阵来说几乎完全适用(可能还要额外定义一下div),而这是过程抽象的好处所带来的。
  如果语言支持,实际上多定义一层mul是完全没必要的,可以直接

  1. define [[X11,X12],[X21,X22]] * [[Y11,Y12],[Y21,Y22]] ->
  2.          [[X11 * Y11 + X12 * Y21, X11 * Y12 + X12 * Y22],
  3.           [X21 * Y11 + X22 * Y21, X21 * Y12 + X22 * Y22]].

  如果这样的话,语言就具有了同数据类型抽象相匹配的过程抽象能力,但很遗憾,目前大多数语言并不从语法层面上具备这种抽象能力(也许erlang比较接近,这篇文章里的伪代码也是类似erlang语法的),所以前面描述的这些东西,是不能够这样被直接实现的,但是,这只是一个开始,我们将在后续的文章里逐步深入介绍怎样改造程序的模式甚至怎样设计一种新的语言,以达到数据和过程抽象相匹配的符号表达能力。
最后,用一个简单实例结束这篇文章,下面的过程直接调用pow计算菲波纳契数列,具体的原理留待下一篇文章解释——

  1. define fib N ->
  2.          [[X11,X12],[Y11,Y12]] = pow([[1,1],[1,0]], N - 1), X11;

月影 Uncategorized

数据抽象和过程抽象

April 2nd, 2010

越来越觉得erlang是个好工具,天生的符号抽象能力,几乎和scheme不相上下

  1. pow(D, N) ->
  2.         pow_iter(normalize(D), D, N).
  3.  
  4. pow_iter(R, _, 0) ->
  5.         R;
  6. pow_iter(R, D, N) ->
  7.         case N rem 2 of
  8.             1 ->
  9.                 pow_iter(mul(R, D), D, N - 1);
  10.             0 ->
  11.                 pow_iter(R, mul(D, D), N div 2)
  12.         end.
  1. normalize([[_X11, _X12],[_X21, _X22]]) ->
  2.         [[1,0],[0,1]];
  3. normalize([_X,_Y]) ->
  4.         [1,1];
  5. normalize(_) ->
  6.         1.
  1. mul([[X11, X12],[X21, X22]],[[Y11,Y12],[Y21, Y22]]) ->
  2.         [[X11 * Y11 + X12 * Y21, X11 * Y12 + X12 * Y22],
  3.          [X21 * Y11 + X22 * Y21, X21 * Y12 + X22 * Y22]];
  4. mul([X1,Y1], [X2,Y2]) ->
  5.         X1 * Y2 - X2 * Y1;
  6. mul(A, B) ->
  7.         A * B.
  1. fib(N) when N > 0 ->
  2.         [[F,_],[_,_]] = pow([[1,1],[1,0]], N - 1),F;
  3. fib(_) ->
  4.         0.

月影 Uncategorized