如果你也会C#,那不妨了解下F#(2):数值运算和流程控制语法

本文链接:http://www.cnblogs.com/hjklin/p/fs-for-cs-dev-2.html

一些废话

一门语言火不火,与语言本身并没太大关系,主要看语言的推广。
推广得好,用的人多,问题也能及时得到解决,用的人就越多,这是一个良性循环,即使语言本身有很多不足也很快能得到解决。
但有的语言本身很好,使用者却不多,缺少交流和推广,致使进入恶性循环。
《黑客与画家》作者把Lisp吹上天,但却没见他继续推广,至今在使用的团队和行业还是很有限。
而说到F#,国内也出过F#的高校教材,不知道是否有高校开课,在企业上更是很少使用。
“赵姐夫”(博客)在10年说过要做F#在国内的推广者,几年过去了,也是无声无息。

那为什么在2016年的现在,那么多新的语言和技术,我们还要来了解F#呢?

  • F#和C#一样,也是基于.Net平台的语言,了解了语法后,就能快速地使用.Net框架甚至C#编写的框架,
    而且在学习过程中.Net框架中很多以前不理解的东西,通过F#就变得很容易理解了。作为.Net程序员,还是值得了解的。
  • 函数式语言的“天然支持异步和并行”的能力,也使得多线程开发变得简单。
    C#在最近的版本中经常得益于F#对.Net框架的推进,如加入了async关键字,有 Tuple了(虽然在语法层面不支持)等等。
  • 在最近发布的.Net Core中,也可以通过dotnet new -l f#来创建F#项目。.Net Core里F#的坑,这里就不细说了。 😅 通过.Net博客,也可以发现越来越多人在使用F#。

F#并无法替代C#,但两者却能起到互补的作用。

记得《七周七语言》里说过:

每学一门新的语言,思维方式都会发生改变。编程语言亦是如此。

作者在书中说,每学习一门语言都会去找互动教程,但大多数情况下并找不到称心如意的。

若想领会一门语言的精髓,它(指互动教程)可就无能为力了。我想要的是那种痛快淋漓、深入探索语言本质的感觉。

我是没有作者的这种能通过探索语言本质的能力来介绍F#,但希望也能让大家发现F#语言的动人心弦之处
废话说得有点多了,下面继续介绍F#吧。

数值运算

我们再翻出上一篇的小例子:

let mutable sum = 0 
for i = 0 to 100 do
    if i%2 <> 0 then sum <- sum + i 
printfn "0到100中的奇数的和为%A" sum ;;

四则运算和模运算(取余)不必多说,但对于浮点数(floatfloat32)类型,还支持**(幂)语法。

2. ** 3.;;  // val it : float = 8.0

abssin等这些基本数学函数在F#里也包含在操作符(Operators)的模块里,所有操作符可在MSDN文章(Core.Operators Module(F#))查看。

大整数(BigInteger)

上一篇的数值类型对比中,我们可以看出,F#在语法层面上支持大整数。
大整数于.Net 4.0添加到框架中,在System.Numerics模块里。F#创建项目时默认引用了此dll。(大整数也支持**运算符,但指数必须为int,其实就是Pow方法。)

let bigInt = 4325I;; //相当于C#的 var bigInt = new BigInteger(4325);
3I ** 8;;  // val it : System.Numerics.BigInteger = 6561 ...

在内存足够的情况下,BigInteger支持任意大的整数。 通过大整数,我们也可以自己实现大实数,或直接使用第三方类库。

数值类型转换

在F#中,没有隐式类型转换,就连intlongfloat也没有。所以,数值类型转换使用返回对应类型的转换函数:

int 2.5;;   // 2
float "3.1415"  // 3.1415
(float 2)**(float 3);;  //8.0

当然也可以使用.Net框架的System.Convert静态类。上述方法在转换字符串时也是调用的此方法。

位运算符

F#中的位运算符均使用三重符号:

运算符 名称 示例 结果
&&& 0b1111 &&& 0b0011 3
||| 0xFF00 ||| 0x00FF 65535
~~~ 取反 ~~~0b0011 -4
^^^ 异或 0b0011 ^^^ 0b0101 6
<<< 左移 0b0001 <<< 3 8
>>> 右移 0b1000 >>> 3 1

注:“或”使用|||,博客园的Markdown渲染时,竟然不支持表格里的|转义。😅

比较

因为函数式语言中,数值不可变,所以=在F#中并不是赋值符号。
=在F#执行相等比较操作,而不等则为<>,这两个符号与C#有异:

5=8 ;;  //false
"ax"<>"ay" ;;   //true

那就会有人说,F#也有可变的值啊,怎么赋值呢!没错,在上面的示例代码sum为可变值,每次循环通过<-符号改变其值。好诡异但又好有道理的符号啊! 😲

所以需要注意,C#中的 int i = 1; 其实是相当于F#中的 let mutable i = 1 。因为C#默认创建的是可变类型。 请查看后面的while循环介绍。

此外,F#中还有一个compare函数用来比较两个值:

compare 31 31;; //  0
compare 5 4;;   //  1
(*
    compare x y
    若x > y则返回1;若x < y则返回-1;若x = y则返回0    
*)

这个比较在C#中有实现过IComparable接口的应该了解,其实compare函数的参数就是需要实现了IComparable接口的类型。
在.Net中,比较是一个复杂的话题,在此就不展开了,可参考MSDN文章(Object.Equals方法IComparable 接口)。

流程控制语法

for循环语句

下面是0到100的循环,从0打印到100:

  • C# 代码:
for (int i=0; i<=100; i++) { System.Console.WriteLine(i); }
  • F# 代码:
for i=0 to 100 do printfn "%i" i
for i in [0..100] do printfn "%i" i
for i=100 downto 0 do printfn "%i" i //逆向循环,从100到0

F#版本中的[0..100]是创建一个从0到100的列表(list),在之后列表模块会进行介绍。
遗憾的是for ... tofor ... downto语句无法创建如for (int i=0; i<=100; i+=2)这种间隔不为1的循环,只能使用for ... in再加上一个列表。

while循环语句

while循环实现的从0打印到100:

  • C# 代码:
int i = 0;
while (i <= 100)
{
    System.Console.WriteLine(i);
    i++;
}
  • F# 代码:
let mutable i = 0
while i <= 100 do
    printfn "%i"
    i <- i + 1

需要注意的是,F#中并没有do...while的循环,在for循环和while循环中,也都没有breakcontinue关键字。
刚开始接触F#或习惯了C#的循环语句可能会不习惯,但当你熟悉了函数式编程的思想后,会发现这些在F#中都是不重要的,甚至循环语句都是不必要的
同时,也尽量不要使用可变类型。因为值的不可变性,使多线程编程变得简单;而在C#中,因为值默认为可变的,使程序在多线程运行中增加了很多不确定因素。

条件语句之if

在上面的示例代码中已经使用过if语句。
在F#中,if语句的完整结构是这样的:

if x>y then "x大于y"
elif x<y then "x小于y"
else "x等于y"

if x>y then printfn "x大于y"  //if分支无返回值

在F#的if语句中,每个分支必须返回相同类型的值,但不需要return关键字(在介绍函数时会涉及)。
如果if分支没有返回值(即返回unit),则else为可选的;否则,必须有else分支。(类似于C#的?:操作符,但F#中没有这个操作符。)
C#中的else if在F#中为elif

条件语句之switch(match)

在C#中,还有一种分支结构的语句是使用switch...case。在F#中类似的语法是match...with

  • C# 代码:
int i = 1;
switch (i)
{
    case 1:
        Console.WriteLine("this is one");
        break;
    case 2:
        Console.WriteLine("this is two");
        break;
    case 3:
        Console.WriteLine("this is three");
        break;
    default:
        Console.WriteLine("this is something else");
        break;
}
  • F# 代码:
let i = 1
match i with
    | 1 -> printfn "this is one"
    | 2 -> printfn "this is two"
    | 3 -> printfn "this is three"
    | _ -> printfn "this is anything else"

其中_为通配符,与C#switch中的default类似。
但F#中的match语句功能非常强大,可查看MSDN文章Match表达式(Match Expressions)
模式匹配(Pattern Matching)
模式匹配在函数式编程中有着作用,在之后的函数式编程中也会进行介绍。

在下一篇中,我们来了解数组和列表,以及一些F#特有的类型。

posted @ 2016-08-14 22:57  忽见客来  阅读(2123)  评论(7编辑  收藏  举报