这是一门很神奇的语言,我真的非常感谢我的师兄(yinzhu)! 是他带领着我进入了一个又一个我以前从没尝试过的领域!我非常尊敬和爱戴他~ 为一生有这样一个师兄而自豪!在他的强烈建议下,认识并学习了F#,特以此记之,视为纪念和感谢!
首先认识的是F#语言和C#语言一样都是运行在.net平台上的,这意味着,这两个语言生成的dll库是可以通用的,我们可以用C#写一个project生成一个dll然后放到F# project中照样可以使用,反之亦然,这就让F#使用面更为广泛!虽然目前F#与C#比起来就像非主流一样。C#有VS强劲的IDE支持,使用C#开发会更为快捷方便,但是C#容易使人思维懒惰!虽然目前VS对于F#的支持还不是很强大与方便,但是F#有很多不同的特性,个人觉得F#适用面更为广阔,而且某些方面F#让人对于语言会有一个深刻而不同的认识!
在各种软件类的语言中,我曾学过pascal,c,java,c#,但是F#不同于以前我学习过的任何语言!因为她是multi-paradigm的,他有三个paradigm,分别是functional programing,object oriented programing, language oriented programming.
(一)对于functional programing的初浅认识:
其中让我印象最深的functional programming!虽然这个概念很早就提出了,但是我对于functional programming的认识,是从这门语言开始的。类似的语言还有OCaml和SML等。当其作为一门函数语言,意味着他拥有这个下面几个特性:
(1)数值不变性 (2)函数可以组合 (3)函数可以当做数值来用
(4)可以是懒惰的 (5)类型匹配
对于functional programming,就是把程序当做一个个function,我们考虑的是每一个function干什么,输入啥,输出啥,如何将一个个function组合起来,来到达我们的目的,而不是像以前写c语言程序等,注重算法和流程。这完全是一个函数的世界!对于F#来说,数值不变性是默认的,数值不变性可以有效的保证函数的安全性,因为我们完全可以把函数看做是一个独立的东西,外部的东西只能通过输入影响它!如果学过硬件,完全可以函数看作是硬件当中的组合逻辑电路!同时这个特性也有益于我们对于函数的测试与检查,这在数值程序中相当重要!对于数值程序,我们就可以一个函数一个函数的检查,所以在数值程序中,变量越少越好!当然了,在某些必要的情况下,弄一些值可变的变量也是合情合理的,因为这能有效的减少代码的复杂度例如:
let square x=x*x
let imperativeSum numbers=
Let mutable total=0;
For I in numbers do
Let x=square i
Total<-total + x
Total
F#中包含层次结构不是用{}符号,而是tab缩进,默认为4个空格。
在很多程序语言中,函数和数值是完全不一样的两个东西,但是在F#中,我们可以把函数当做数值一样,因此,函数可以像数值一样当做另一个函数的输入~例如:
(1)
let negate x=-x
List.map negate [1..10]
同样可以写成:
(2)
List.map (fun x-> -x) [1..10]
F# interactive结果:val it : int list = [-1; -2; -3; -4; -5; -6; -7; -8; -9; -10]
上面提到了F# interactive,我们可以把这个当做一个即时解释器,我们在写F#代码的时候,可以不需要对整个程序进行执行,我们只需要选中其中的部分代码,例如选中一个刚定义好的function,然后贴到F# interactive,即可立即编译执行,看到结果。这对于数值程序检查单个function很有效!恩,老是提数值程序,相信你一定联想到matlab了吧,这个F# interactive就很像matlab中的Command Window!在matlab中写代码的时候,也可以选中部分语句,然后按F9快捷键,就可以立即执行这部分代码!上面还用到了List.map这个东西,如果你是C#的程序员一定很熟习List这个东西,没错!这个正是.net平台上的,所以除了在C#中可以使用,F#中同样可以!对于“fun x -> -x”,理解为生成一个没有名字的函数,不用let声明是因为假如简单的函数也用let绑定的话,会使得程序有很多小函数。。
哈,你可能会说,这有啥,很多程序语言都能办到这种将函数作为参数传到另一个函数,那如果我告诉你函数返回函数呢?
例如:
(1)
let generatePowerOfFunc base=(fun exponent -> base ** exponent)
let powerOfTwo=generatePowerOfFunc 2.0
powerOfTwo 8.0;;
F# interactive结果:val it:float = 256.0
let powerOfThree =generatePowerOfFunc 3.0
powerOfThree 2.0;;
F# interactive结果: val it:float = 9.0
恩,是不是很神奇,哈哈,这个东西以前我可从没接触过!虽然后来接触了,用的也不是很多,哈哈。
在F#中有我感到一个符号很有意思,那就是管道符号,其中最常用的是这个”|>”,他的定义是:let (|>) x f = f x 简单来说就是将函数f的参数提前了,还是看例子比较好理解:
let getFiles folder=
Directory.GetFiles(folder, “*.*”, SearchOption.AllDirectories)
let totalSize =
folder
|>getFiles
|>Array.map (fun file -> new FileInfo(file))
|>Array.map (fun info -> info.Length)
|>Array.sum
其中每上一层的结果就是下一层函数的输入。
(注:Array.map (fun file -> new FileInfo(file)) 可以当做一个函数,还记得前面提到的函数返回函数吗?当然,如果你理解为上一层的结果是下一层的函数的最后一个输入参数也不无不可。。例如理解为Array.map的最后一个参数)
(二)另外一个让我印象深刻的就是F#的pattern matching
正因为这个东西的存在,让我们省了好多代码。恩,先来几个简单的例子:
(1)
let isOdd x = (x % 2 ==1)
let describeNumber x =
match isOdd x with
| true -> printfn “x is old”
| false -> printfn “x is even”
(2)
Let testAnd x y=
match x,y with
| true,true -> true
| true,false-> false
| false,true -> false
| false,false -> false
哈,是不是觉得别的语言中case似乎也能办到呀,那再来些好玩一点的
(3)
let rng= new Random()
let secretNumber=rng.Next() % 100
let rec highLowGameStep ()=
printfn “Guess the secret numbe:”
let guessStr = Console.ReadLine()
let guess = Int32.Parse(guessStr)
match guess with
| _ when guess > secretNumber
-> printfn “The secret number is lower.”
highLowGameStep()
| _ when guess = secretNumber
-> printfn “You’ve guessed sorrectly!”
highLowGameStep()
| _ when guess < secretNumber
-> printfn “The secret number is higher.”
highLowGameStep()
(注:rec表示该函数是递归函数,‘_‘是一个通配符,指的是能匹配任何东西,不过每一次匹配需要when的判断 )
(4)
let describeNumbers x y =
match x,y with
|1,_
|_,1
-> “One of the numbers is one”
|(2,_) & (_,2)
-> “Both of the numbers are two”
| _ -> “Other.”
其实关于match绑定的好玩例子太多了,除了通过match语句实现匹配,let语句中同样包含着匹配
(5)
Let x,y=(100,200)
更绝的是如果匹配用在了list类型里面,那将会省很多事
(6)
let rec listLength theList=
match theList with
|[] ->0
|[_] ->1
|[_;_] ->2
|[_;_;_] ->3
|hd::tail ->1+listLength tail
(注:在F#中有一个基本类型list,此类型不同于.net库中的List,list中的元素值不允许改变,一般形式为[1;2;3],即元素间用分号隔开)
(注,hd,tail是随便起的名字,用任何变量名都可以,::是list的连接符,在其前面的表示list的第一个元素,后一个代表出除第一个元素外剩下的链表)
关于pattern matching的先说这么多
(三)F# Discriminated Unions
Discriminated Union在F#中是一个基本的类型定义一般是下面这个样子的
(1)type Suit=
| Heart
| Diamond
| Spade
| Club
指的是对于Suit类型来说,其值可以是Heart,Diamond,Spade,或者是Club,恩。。这个不同于别的语言的enum,他厉害多了。。同样的,也不同于别的语言(c语言)那种共用一个空间的Union。举个例子:
(2)
type BinaryTree =
| Node of int *BinaryTree*BinaryTree
| Leaf
let rec printInOrder (tree:BinaryTree)=
match tree with
| Node (data,left,right)
-> printInOrder left
Printfn “Node%d” data
printInOrder right
| Leaf
-> ()
(注:类型BinaryTree有两种,可以是Node也可以是Leaf,如果是Node,那么会包含有(int,BinaryTree,BinaryTree)元组)
let binTree=
Node(2,
Node(1,Leaf,Leaf),
Node(4,
Node(3,Leaf,Leaf),
Node(5,Leaf,Leaf)
)
)
printInOrder binTree
F# interactive结果:
Node1
Node2
Node3
Node4
Node5
看到这个树的例子,假如仔细想想,会惊人的发现这和编译器的前端所用到的那些东西有相当大的联系!假如使用F#做一个编译器或许是一个非常好的选择!!
唉。。关于F#精彩有趣的地方太多了。。原来写点东西是那么累。。我开始有点佩服那些写书的人了。今天就先记这么多。
参考资料:
[1]Programming F#---Cbris Smitb foreword by Don Syme
[2]Tomas Petricek([email protected]) http://tomasp.net/blog