前面三篇是关于javascript的函数式特性:
索性就把形式化方法从头复习一遍吧,顺便也温习下f#,并尝试使用javascript来表现一下,同时也会加入C#作为对比。关于f#,请阅读维基百科:F#
为什么要学习函数式编程?原因有太多,每个人都有他们的说法。而对于我,因为当我第一次在C#中使用LINQ、使用Lambda时,感到非常惊讶,怎么会有这样的用法?但当时仅仅停留在使用的层面,没能理解为什么,或者是怎么做到的。直到学习了这门课程之后,我才真正明白。所以这就是原因,很简单。
基础
首先是基础部分,看一下如何定义类型。比如我们要定义名为"day"的类型。这个"day"可以是Monday,Tuesday,...,Sunday。下面呢,看看三个版本的定义,首先是F#:
1 type day = 2 | Monday 3 | Tuesday 4 | Wednesday 5 | Thursday 6 | Friday 7 | Saturday 8 | Sunday
在F#中,type是定义类型的关键字,定义的构造函数的首字母必须大写如应该是"Monday"而不是"monday"。"|"后面的每一个值都是day类型的。第二个是javascript版本的:
1 var day = function(name){ 2 this.name = name; 3 } 4 var Monday = new day("Monday"), 5 Tuesday = new day("Tuesday"), 6 Wednesday = new day("Wednesday"), 7 Thursday = new day("Thursday"), 8 Friday = new day("Friday"), 9 Saturday = new day("Saturday"), 10 Sunday = new day("Sunday");
在javascript中,day作为一个类,然后其他都是day的实例,跟F#比起来是有点麻烦。这应该是一种表现,有其它更好的方式还望高手告知。最后呢是我们最熟悉的C#啦:
abstract class day { } class Monday:day { } class Tuesday:day { } class Wednesday:day { } class Thursday:day { } class Friday:day { } class Saturday:day { } class Sunday:day { }
在C#代码里,使用了抽象类做类型,其他都从它继承。很明显的,在表达能力上F#最简洁。有了类型后,再定义一个使用此类型的函数:
1 let next_weekday (d:day) : day = 2 match d with 3 | Monday -> Tuesday 4 | Tuesday -> Wednesday 5 | Wednesday -> Thursday 6 | Thursday -> Friday 7 | Friday -> Monday 8 | Saturday -> Monday 9 | Sunday -> Monday
上面定义一名为"next_weekday"的函数,后边括号中接受一个参数"d","d:day"中冒号后边指的是类型,最后的" : day"表示函数的返回类型是day,"="后面很明显就是函数体啦。函数体中"Monday -> Tuesday"中的箭头右边表示返回值。所以这个函数就是下一个“工作日”,周一的下一个工作日是周二,那“箭头”是最自然、直接的表达方式,一目了然。再看看其它两个版本的:
1 function next_weekday(d){ 2 switch(d.name){ 3 case "Monday": return new day("Tuesday"); 4 case "Tuesday": return new day("Wednesday"); 5 case "Wednesday": return new day("Thursday"); 6 case "Thursday": return new day("Friday"); 7 case "Friday": return new day("Monday"); 8 case "Saturday": return new day("Monday"); 9 case "Sunday": return new day("Monday"); 10 } 11 }
day next_weekday(day d) { switch(d.ToString()) { case "Monday": return (new Tuesday()); case "Tuesday": return (new Wednesday()); case "Wednesday": return (new Thursday()); case "Thursday": return (new Friday()); case "Friday": return (new Monday()); case "Saturday": return (new Monday()); case "Sunday": return (new Monday()); default:return null; } }
这样定义完了函数,就可以先做一个单元测试。同样的先看F#的:
printfn "%A\n" (next_weekday Friday) printfn "%A\n" (next_weekday (next_weekday Saturday)) //结果: Monday Tuesday
同样地,javascript与C#的测试:
console.log(next_weekday(Friday)); console.log(next_weekday(next_weekday(Saturday))); //结果: day {name: "Monday"} day {name: "Tuesday"}
1 Console.WriteLine(next_weekday(new Friday())); 2 Console.WriteLine(next_weekday(next_weekday(new Saturday())));
//结果:
Monday
Tuesday
测试结果都是OK的啦。使用F#又是最简洁的哦。而且语义上面也是最直接的。不知道你是否会爱上F#,嘿嘿。再来定义一个类型,只有True和False的Boolean类型。大家都知道这个类型除了C语言没有,其他语言都内置了,现在我们就自己来实现一下:
type bool = | True | False
var bool = function(b){ if(b==="True" || b==="False") this.name=b; }
abstract class bools { } class True { } class False { }
其中C#中bool为关键字,所以把它改成了bools。如果抛开逻辑,只看类型貌似没啥意义,根本体现不出布尔的特性,所以加上一些辅助函数把这些特性表现出来。他们别是:取反、或和与操作。
let negb (b:bool) : bool = match b with | True -> False | False -> True let andb (b1:bool) (b2:bool) : bool = match b1 with | True -> b2 | False -> False let orb (b1:bool) (b2:bool) : bool = match b1 with | True -> True | False -> b2
function negb(b){ if(b.name==="True") return new bool("False"); if(b.name==="False") return new bool("True"); } function andb(b1,b2){ if(b1.name==="True") return b2; else return new bool("False"); } function orb(b1,b2){ if(b1.name==="True") return new bool("True"); else return b2; }
bools negb(bools b) { if(b.GetType().ToString()=="False") return new True(); return new False(); } bools andb(bools b1,bools b2) { if(b1.GetType().ToString()=="False") return new False(); return b2; } bools orb(bools b1,bools b2) { if(b1.GetType().ToString()=="True") return new True(); return b2; }
测试和结果就不贴了,上面主要做了一些类型定义方面的工作。感觉javascript在实现的时候有点奇怪,各读者心理也明白这是弱点,也是优点!F#在做类型定义的时候非常直接,简洁。而C#主要就是通过结构体或类来做定义。还是F#更让人喜欢:)