我的博客

Another Joycode.MVC Powered Blog
随笔 - 7, 评论 - 0, 引用 - 0

导航

每月存档

广告

 

原文出处:http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!725.entry

Translated By Sean Wang

如果你有C #代码并且打算直接翻译成F # , 最终生成的F#代码可能不太符合语法习惯或者不太好。然而,如果你对一门语言(F#)来说是个新手,有时了解一下如何在不懂语法的情况下将一门众所周知的语言(C#)翻译出来是有益处的,不过希望这不要阻止你在(学习新语言方面)取得进步。因此,今天的博文主要采取以下形式:“这里有些C #代码,我怎么写成F # ” 。它的目的是用作参考或休闲阅读,在其中发现一些很少使用或不太知名的F #语法/运行/功能。请不要将此博文用来学习语言,一定要使用那些有益的F#教程,或是样本上的网页,这篇博文只是用来弥补不足。
今天的博文,只包括一些方法内部的代码,所以你不会在这儿找到任何有关声明类和命名空间的内容。取而代之的,我将涵盖以下的C #内容 :
类型转换
操作符
表达式
声明
并且指出如何将这些C #语句翻译成相应的F # 。在此过程中我会加入一些注释,说明什么是好的,不好的,合乎语法的,等等,但我不会涉及太多细节。如果我说的不够通俗,或者是有什么没说清楚,尽管把问题发表在下面的评论栏里。
不再进一步使用ADO !
类型转换
根据源类型和目的类型,类型转换“操作符”(即: “ (类型名)表达式” )在C #中可以代表各种不同的内容。我即将实现4种最常见的类型转换:
// C#
char c = 'a';
int x = (int)c;         // numerical conversion
object o = (object)c;   // boxing conversion
    Dog d = new Dog();
    Animal a = (Animal)d;   // upcast
    d = (Dog)a;             // downcast (may fail at runtime)
以下是相应的F #代码,跟着的是一些注释。
// F#
let c = 'a'
let x = int c        // numerical conversion
let o = box c        // boxing conversion
let d = new Dog()
let a = d :> Animal  // upcast
let d2 = a :?> Dog   // downcast (may fail at runtime)
数值转换在F #通过Library函数实现。例如,函数“int”将字符型(或float或unit或任何)转换成int 。反过来说,这些目的类型也有类似的功能,例如函数“char”可以将传给它的实参转换成char类型。你可以在这里找到更多一些关于这些函数的内容:Library Reference。请注意, “枚举”函数能够(将实参)转换枚举类型;具体枚举类型推断,有时需要类型说明,比如
let y : MyEnumType = enum 0 // F#: y is 0-value of MyEnumType
装箱是将一种原始数据转换为一个对象。由F#函数 “box”实现 。事实上, “box” ,将任何类型向上转换为System.Object (在F #中用缩写名称“obj” ) 。
向上转换 :如果您想向上转换类/接口等级,使用的F #语法“表达式:>类型“ 。如果编译成功,这个转换在运行时也会成功。 (注: 在F# 1.9.6.2中 ,表达式优先级有时有些奇怪,所以有时您可能需要在外面加括号,如“ (表达式:>类型) “ ,以使代码解析正确。 )这个操作符很少会用到。
向下转换:如果您想向下转换一类/接口等级,使用的F #语法“表达式:?>类型“ 。问号在这个里是为了提示操作可能会失败,就像在C #中,可能会得到一个InvalidCastException 。你很少会使用此操作符,在C #中通常选择“is”和“as” ,看下面这些表达式在F#中是如何表达的。
操作符
许多算术(如+ , * )和条件(如& & , | | )操作符在F#和C #里是一样的 。至于不同的地方,最常见的如下: ( 1 )逻辑非:在C #中写成“ ! ” ,而在F #写成“not” ; ( 2 )相等判断,在C #中是“ == ” 而在F#中只是“ = ” ;和( 3 )不等,在C #中有“ ! = ”而F #中就是"<>". (至于逻辑非,注意在F#中, “ ! ”已经有一个和引用类型有关的含义,这是继承自OCaml的。 )其他有些通用的地方,如位逻辑运算符在C#中通常是一个字符(如“ | ”和"&"),而在F #中是三个字符("|||"和"&&&");你在使用如“Flag”枚举时会需要这些操作符。
大多数F#操作符被重载了,在Visual Studio中,像下面这样指向代码时,会有类型推断,不要误解了:
let f x y = x + y  // hover mouse over f, says int -> int -> int

这并不意味着“ + ”只适用于ints ;您可以使用“ + ”来添加两个浮点型,或两个字符型,甚至两个字符串。(至于这一使重载究竟是如何工作的,这里面牵涉到”内联”和”^a”类型。对这里面的细节问题暂时不要研究太深,这会让你学的更愉快些;当对类型没有指定时,语言/工具提示一般选择“整数”作为默认类型,以便隐藏内部的复杂机制。 (”如何重载常用操作符,诸如’+’”,另一个更好的“解决方案”,就是要利用类类型,但是,无论是CLR还是F#都没有提供充分的类型系统去处理类类型. ) )
在C #中,操作符('=')一样是用于初始化和破坏性赋值:
int i = 3;    // initialization
i = 4;        // destructive assignment

在F # 中,这些都是独立的操作符( ' = '和'<-'),只有可变变量接受赋值:
let i = 3 // initialization
    i <- 4 // does not compile, i is immutable
let mutable j = 3
    j <- 4 // destructive assignment

表达式
大多数表达式涉及结构对象,方法和属性,看起来在C #和F #中一样 :
new Dog()  // constructor
s.StartsWith("h", StringComparison.Ordinal)  // method call
s.Length   // property
而一个显著的区别,就是从两种语言的语法上说,数组及其他类型的索引各不相同。在C #中:
var dict = new Dictionary<string, int>();
    dict["foo"] = 42;
    Console.WriteLine(dict["foo"]);
F#中方括号前要加点:
let dict = new Dictionary<string,int>()
    dict.["foo"] <- 42
    printfn "%d" dict.["foo"]

C#中有着大量的表示兰姆达/委托的不同语法,举一个例子:
    Func<int, int, int> f = (x, y) => x + y;  // C# lambda;

F #大致对应如下 :
let f = (fun x y -> x + y) // F# lambda

在表达兰姆达/函数/委托方面,还有很多有趣的区别,我今天就不深入讨论这其中的细节了。
在C#和F#中都有”typeof”操作符返回System.Type,但C#中用圆括号,但F#中用尖括号:
typeof(int)  // C# typeof

typeof<int>  // F# typeof

另一个区别涉及泛型; 在C #中,您可以省略类型说明获得一般性的定义:
Console.WriteLine(typeof(List<int>).IsGenericTypeDefinition); // false
Console.WriteLine(typeof(List<>).IsGenericTypeDefinition);    // true而F #有一个单独的操作符,就是所谓的的“typedefof ”, 获得未实例化的泛型:
printfn "%A" (typeof<List<int>>.IsGenericTypeDefinition) // false
printfn "%A" (typedefof<List<_>>.IsGenericTypeDefinition) // true

C #中有“is”和“as”操作符可以作类型测试。 F #对此采用一个特定的模式。所以这段C #代码:
if (animal is Dog)
    {
        Dog dog = animal as Dog;
// ...
    }
else if (animal is Cat)
    {
        Cat cat = animal as Cat;
// ...
    }

改成F #代码应该是:
match animal with
| :? Dog as dog -> // ...
| :? Cat as cat -> // ...

其中":? type”是一种类型测试,如果那个类型测试成功了,”as 标识符” 就将当前值命名,名字就是as后面的标识符。 (另外说一句: F#中 “else if”可简称为“ elif ” 。 )
C #中有条件表达式,三元运算符“ ? : ”:
    condition ? trueVal : falseVal
F #也有相同的操作符,但它的名字是if-then-else:
if condition then trueVal else falseVal

(请注意,”if”在F#中用的比C#中要少一些,在F #中 ,许多条件表达式都是这样通过模式匹配,而不是if-then-else。 )
C #中有一个操作符”default”, 返回给定类型的零初始化值:
default(int)
它的用处是有限的;最常见的是,你可以在泛型中使用default(T)。 F #在库函数中也有类似的构造:
     Unchecked.defaultof <int>
这些构造在C#和F#中使用的都很少。
语句
本节说明如何将C#语句翻译成F#.F#本身没有语句;一切都是表达式, 对一个方法体求值就意味着对它的表达式求值.表现形式。在F#中,表达式里最像语句的就是”()”的,这个值属于”unit”类型,类似于C#中的void。
C #中有三种循环结构:
// C# loops
while (condition)
    {
        SomeCode();
    }
foreach (var e in someEnumerable)
    {
        SomeCode();
    }
for (int i = 0; i < 10; ++i)
    {
        SomeCode();
    }

F#只有”While”和”Foreach”(在F#中拼写为”for”); 一个C#”for”循环通常被模拟为一个”范围”:
// F# loops
while condition do
        SomeCode()
for e in someEnumerable do // foreach
        SomeCode()
for i in 0..9 do // compiles like a C# for loop
        SomeCode()
F #没有类似”break”或”continue”这样的语句,必须用控制流或布尔变量去模拟这种结构。 “While” 循环在F#中式很少见的,因为这必须在条件中包含可变变量, 而F#通常避免可变(变量等),而是通过递归实现简单的循环.即使是for(each) 循环也很少出现在F#中, 因为有时F#更倾向于这种形式:

someEnumerable |> Seq.iter (fun e -> SomeCode())

(特别是Seq.iter的实参很短的时候)
C #中有一个switch语句。它看起来像这样:
switch (x)
    {
case 1:
            SomeCode();
break;
default:
            SomeCode();
break;
    }

在F #中,可以通过模式匹配更简洁的表示C#的许多语句,switch只是其中一种:
match x with
| 1 -> SomeCode()
| _ -> SomeCode() // _ is a 'catch all' default
C #中有一个“返回”关键字,退出目前的函数。F#中没有相应的构造。请注意, “返回”关键字在F#中是计算表达式语法的一部分(简称“工作流” ,又名“ monads ” ) ,和C#中的”返回”没有任何关系。和C#中的”break”一样 , 在F#中 您需要使用控制流结构模拟“发挥”以实现提前退出的功能。 (虽然我偶尔希望在F#中有”break”,但我从来没有这样想过”return” –我想不起来有任何地方需要它。 )
在C #中,你通过”throw”关键字抛出异常。而在F #中 ,则使用“raise”实现这个功能:
throw new Exception("boom");    // C#

    raise <| new Exception("boom") // F#

在F#中,这是为数不多的我认为应该使用向后管道操作符”<|”的地方. 如果没有它,F#的优先级规则要求我们,必须用小括号括住这个表达式:
    raise( new Exception("boom") ) // F#

我想大多数人都会认为这样看起来很笨拙。
C #中有一个try -catch-finally的异常处理。你可以像这样写:
try
    {
        SomeCode();
    }
catch (NullReferenceException nre)
    {
// swallow it
    }
catch (Exception e)
    {
throw;
    }
finally
    {
        SomeOtherCode();
    }

相对应的,在F #中,代码将是:
try
try
            SomeCode()
with
| :? NullReferenceException as nre -> () // swallow it
| e -> rethrow()
finally
        SomeOtherCode()
在这里,有些东西我必须指出来。首先, F#中,try-with 和 try-finall是两个不同的构造-没有try-with-finally。在实践中,你通常只能做其中一个(一般你使用try-finally的频率几乎是try-catch的十倍,无论是哪一种语言-捕捉异常很少是正确的事情) 。 F # “catch”模块,只需使用模式匹配检测异常的类型,就像我们前面看到的C#中“is”/“as”的翻译一样。在F #中 ,重新抛出的异常,请使用“ rethrow ( ) ”。
C #中有一个“checked”语句检查算术溢出,F#在库中有checked操作符,所以如果你需要溢出检测,只需打开此命名空间,并使用该操作符就行了。
C #中有一个“lock”语句可以锁定对象的一个关键部分:
lock (o)             // C# lock
    {
        SomeCode();
    }
的F # , “lock”是一个函数,可以用来锁定对象和一个兰姆达表达式的关键部分:
lock o (fun () -> // F# lock
        SomeCode()
)
C #中有对于IDisposables的using语句 :
using (var disp = someDisposable)
    {
        SomeCode();
    }
F #的相应的语法:
    use disp = someDisposable 
    SomeCode()
F #的“use”相当于“let” ,它的范围一直到周围块的结尾。

在C#方法中返回IEnumerabl, 可以通过下面的的”yield”语句实现:

yield return 42;  // C# yield a value
yield break;      // C# end the enumeration

在F#中,你不需要声明method-returning-IEnumerable,取而代之的,你可以在任何地方使用”seq{…}”产生序列表达式:
let myIntSeq : IEnumerable<int> = seq { yield 42; }
我加入了类型说明,目的是提醒你”seq”在F#中代表“ IEnumerable ” 。因此,上面那行代码右边的序列表达式,对IEnumerable <int>求值产量一个单一的值( 42 ) 。就像C#代码中的迭代块一样,在F#序列表达式中,你可以通过多种形式使用yield。但是对于C#中的”break”,F#中没有相应的”yield break”。
结尾
我想,到目前为止,这篇博文只是涵盖了C#表达式和语句中所有有趣的内容,阐述了F#中相对应的部分。我希望这对于你学习F#知识,能够起到有益的补充作用.

打印 | 张贴于 2009-10-21 10:29:17 | Tag:暂无标签

留言反馈

暂时没有留言纪录
博客主人设置本博客不允许匿名用户发表言论,请登录后再试

Powered by: Joycode.MVC引擎 0.5.2.0