如何学习 F#






3.58/5 (7投票s)
解释了学习 F# 语言的一些方法的文章。
前言
根据那些致力于设计一种同时使用命令式和函数式编程的语言的人的说法,F# 是一种面向 .NET Framework 的类型化函数式编程语言。然而,虽然 F# 是函数式或过程式的,但其在类型推断、面向对象编程和动态语言技术方面的方法与其他所有主流函数式语言都截然不同。F# 在某种程度上是当今编程技术的典型代表,因为它建立在抽象之上。其理念似乎是,抽象层级越高,就越能摆脱低级复杂性和易出错的代码。F# CTP 插件可以安装在 Visual Studio 2008 上,并且默认包含在 Visual Studio 2010 中。需要注意的是,F# 有一个严格、有序的项目文件管理机制。当作为插件安装时,F# 会提供一个名为 F# Interactive 的实用工具。当你按下 Ctrl+Alt+F 组合键时,它会出现在编码平台的底部。在该窗口中,你可以通过按下 Alt+Enter 来突出显示单个代码块。IDE 上的代码会传输到 F# Interactive 窗口,然后执行代码的功能。通过这种方式可以轻松捕获错误,尤其是缩进错误。
你不能在没有缩进子例程的情况下编写代码。但这些代码块会被计算执行。对于 C# 和 Visual Basic 等其他 .NET 语言,你必须等到整个源文件编译完毕才能捕获错误或处理未处理的异常。那么如何才能扎实地掌握这门语言呢?F# 似乎有不同的特性和不同的构建技术。有一个网络广播链接到 http://blogs.msdn.com/chrsmith/archive/2008/05/02/f-in-20-minutes-part-i.aspx 页面,位于 http://channel9.msdn.com/pdc2008/TL11/。这个由 Luca Bolognese 主持的网络广播以介绍开始,然后重点关注 F# 代码、它是什么以及如何将其用于实际应用。这是一个必看的。本文将解释 F# 的基础知识:如何编写 F# 代码,使用为交互式设计的脚本,一旦脚本正确就可以编译成一个完整的文件,以及如何掌握那个交互式实用工具。例如,考虑这个基本代码片段
#light
open System.IO
let rec allFiles dir =
Seq.append
(dir |> Directory.GetFiles)
(dir |> Directory.GetDirectories |> Seq.map allFiles |> Seq.concat)
在检查这段代码在 IDE 和交互模式下的显示效果之前,让我们先看看语法。令许多人惊讶的是,源代码执行有一个主函数入口点。关键字 "let
" 将一个值绑定到一个符号。因为它绑定了值而不是赋值,所以该值不能被更改。"|>" 运算符是管道运算符。下面是一张幻灯片,总结了管道运算符的工作原理。“dir
” 关键字通过管道传递到 GetFiles
(它包含目录),然后再通过管道传递到获取目录。然后它们被收集为字符串序列并连接起来。F# 被称为类型化函数式语言的原因之一是,当你将鼠标光标悬停在值上时,它会告诉你其参数化类型。例如,当代码被突出显示,我按下 Alt+Enter 将代码传递到交互模式时,打印出的第一个语句是
val allFiles : string -> seq<string>
关键字 val
表示值,allFiles
是一个 string
,它通过管道(可以看作是推入)一个 string
序列,这是一个目录的递归列表。突出显示代码后,我按下 Alt-Enter,代码就被传输到了交互模式。在交互提示符 (>) 下,我输入了 allFiles @”C:\windows\system32”;;(注意双分号)。这是将代码传递到交互模式然后输入带目录路径作为参数的函数时的输出
val allFiles : string -> seq<string>
> allFiles @"C:\windows\system32";;
val it : seq<string>
= seq
["C:\\windows\\system32\\12520437.cpx";
"C:\\windows\\system32\\12520850.cpx";
"C:\\windows\\system32\\7B296FB0-376B-497e-B012-9C450E1B7327-2P-0.C7483456-A289-439d- 8115-601632D005A0";
"C:\\windows\\system32\\7B296FB0-376B-497e-B012-9C450E1B7327-2P-1.C7483456-A289-439d-8115-601632D005A0";
...]
>
关键字 val
表示值,rec
表示递归。当没有定义类型时,默认的词是 'it
'。下面是 IDE 和 F# Interactive 的视图

|> 被称为管道运算符。除了关键字 let
,管道运算符是 F# 中最重要的运算符之一

空白很重要
像 C# 这样的其他语言使用分号和花括号来指示语句和代码块何时完成。然而,程序员通常会缩进代码以使其更具可读性,因此这些额外的符号通常只会为代码添加语法混乱。在 F# 中,空白——空格和换行——很重要。F# 编译器允许你使用空白来分隔代码块。例如,任何比 if
关键字缩进更多的内容都被视为 if
语句体的一部分。由于制表符可以表示任意数量的空格,因此在 F# 代码中禁止使用制表符。因此,让我们看一个演示这一事实的代码示例
// normally those symbols would not indent to the left, as they are elif and else block
let encode (n: int32) =
if (n >= 0 && n <= 0x7F) then [ n ]
elif (n >= 0x80 && n <= 0x3FFF) then [ (0x80 ||| (n >>> 8)) &&& 0xFF; (n &&& 0xFF) ]
else [ 0xC0; ((n >>> 24) &&& 0xFF);((n >>> 16) &&& 0xFF);((n >>> 8) &&& 0xFF);(n &&& 0xFF) ]
现在我们突出显示这段代码,按下 Alt+Enter 组合键,在插入 encode 32;; 之前我们会得到一个类型定义,然后按 Enter 键获得计算结果
> encode 32;;
val it : int32 list = [32]
> encode 320;;
val it : int32 list = [129; 64]
> encode 320000;;
val it : int32 list = [192; 0; 4; 226; 0]
>
F# 倡导函数式编程与命令式编程。下一个示例将接收一些数据(整数),然后将序列中的每个元素平方,以便将这些平方相加。我将通过两种结构来呈现这个例子,以解释 F# 的工作原理。
// the #light directive was taken from the language used to form the design of F#
let data = [1.;2.;3.;4.]
let square x = x * x
let imperativeSum numbers =
let mutable total = 0.0
for i in numbers do
let x = square i
total <- total + x
total
let functionalSum numbers =
numbers
|> Seq.map square
|> Seq.sum
当发送到 F# Interactive 时,我们首先得到
val data : float list
val square : float -> float
val imperativeSum : seq<float> -> float
val functionalSum : seq<float> -> float
我们首先注意到,尽管数据被声明为整数,但整数左侧的小数点使它们成为浮点整数。因此,当我们用该列表绑定值数据时,我们开始时有一个浮点列表。当执行一个操作时,我们使用 -> 运算符(将其“推入”操作)。这给了我们 float -> float,依此类推。结果如下
> imperativeSum;;
val it : (seq<float> -> float) = <clo@0>
> imperativeSum data;;
val it : float = 30.0
> functionalSum data;;
val it : float = 30.0
>
请注意,命令式代码块产生的与函数式编程块的结果相同。
历史
- 2010 年 2 月 21 日:初次发布