正文
F# 语法概览
小程序:扫一扫查出行
【扫一扫了解最新限行尾号】
复制小程序
【扫一扫了解最新限行尾号】
复制小程序
F#和C#的语法差别
语法上,F#和C#有两个主要差别:
- 用缩进而非花括号分隔代码块
- 用空白而非逗号分隔参数
F#常见语法元素
以下是F#代码中常见的语法元素
注释
// 这是单行注释
(* 这是多行注释
第二行
最后一行 *)
let 绑定
let myInt =
let myFloat = 3.14
let myString = "hello"
上面的语句没有显式指定 myInt, myFloat, myString 的类型,类型由编译器推断。
列表
let twoToFive = [;;;] // 方括号表示列表,元素用分号分隔
let oneToFive = :: twoToFive // 符号 :: 将值添加到列表头部,得到新列表,结果为 [1;2;3;4;5]
let zeroToFive = [;] @ twoToFive // 符号 @ 连接两个列表,得到新列表,结果为 [0;1;2;3;4;5]
务必注意,列表元素使用分号分隔,而非逗号分隔。
函数
命名函数用 let 关键字定义,匿名函数用 fun 关键字定义。
let square x = x * x // 使用 let 定义命名函数,函数形参不用小括号围住
square // 运行函数,实参也没有小括号 let add x y = x + y // 不可使用 (x,y)
add // 运行函数 // 多行函数,用缩进,不用分号
let evens list =
let isEven x = x% = // 内部函数
List.filter isEven list // List.filter 是库函数 evens oneToFive // 运行函数 // 用小括号指明优先级
let sumOfSquaresTo100 =
List.sum (List.map square [..]) // 如果没有小括号,那么 List.map 会是 List.sum 的参数 // 管道 |>,将操作的输出传给下一个操作
let sumOfSquaresTo100piped =
[..] |> List.map square |> List.sum // 用 fun 关键字定义拉姆达(匿名函数)
let sumOfSquaresTo100withFun =
[..] |> List.map (fun x->x*x) |> List.sum
模式匹配
match..with 用于模式匹配
// 类似于 switch/case
let simplePatternMatch =
let x = "a"
match x with
| "a" -> printfn "x is a"
| "b" -> printfn "x is b"
| _ -> printfn "x is something else" // 下划线匹配任意值 // Some(..) 和 None 有点像可空类型(Nullable)
let validValue = Some()
let invalidValue = None // match..with 匹配 "Some" 和 "None",同时从 Some 中取出值
let optionPatternMatch input =
match input with
| Some i -> printfn "input is an int=%d" i
| None -> printfn "input is missing" optionPatternMatch validValue
optionPatternMatch invalidValue
复杂类型
复杂类型是指元组,记录和联合
// 元组,包含有序但未命名的值,值之间用逗号分隔
let twoTuple = ,2 // 二元组
let threeTuple = "a",,true // 三元组 // 记录,包含命名的字段,字段之间用分号分隔
type Person = {First:string; Last:string}
let person1 = {First="john"; Last="Doe"} // 联合,包含选项,选项之间用竖线分隔
type Temp =
| DegreesC of float
| DegreesF of float
let temp = DegreesF 98.6 // 类型可以递归的组合,例如,下面的 Employee 联合包含 Employee 类型的列表
type Employee =
| Worker of Person
| Manager of Employee list
let jdoe = {First="John";Last="Doe"}
let worker = Worker jdoe
格式化输出
printf 和 printfn 向控制台输出,类似于 C# Console 类的 Write 和 WriteLine 方法。sprintf 输出到字符串,类似于 String 类的 Format 方法。
printfn "Printing an int %i, a float %f, a bool %b" 2.0 true
printfn "A string %s, and something generic %A" "hello" [;;;] // 复杂类型具有内置的美观输出格式
printfn "twoTuple=%A,\nPerson=%A,\nTemp=%A,\nEmployee=%A" twoTuple person1 temp worker let str1 = sprintf "Printing an int %i, a float %f, a bool %b" 2.0 true
let str2 = sprintf "A string %s, and something generic %A" "hello" [;;;]
比较F#和C#:计算平方和
C#版,不使用 linq
public static class SumOfSquaresHelper
{
public static int Square(int i)
{
return i * i;
} public static int SumOfSquares(int n)
{
int sum = ;
for (int i = ; i <= n; i++)
{
sum += Square(i);
}
return sum;
}
}
F#版
let square x = x * x
let sumOfSquares n =
[..n] // 创建 1 到 n 的列表
|> List.map square // 对每个元素求平方,得到新列表
|> List.sum // 将平方列表求和
F# 语法噪音小,没有花括号,分号,并且在这里不需要显式指定类型。
C#版,使用 linq
public static class FunctionalSumOfSquaresHelper
{
public static int SumOfSquares(int n)
{
return Enumerable.Range(, n)
.Select(i => i * i)
.Sum();
}
}
使用 linq 改写的 C# 代码简洁很多,但不能消除语法噪音,参数和返回值的类型也是必须的,仍然不如 F# 版简洁。
例子:快速排序算法
快速排序算法的步骤是:
- 从列表中取出一个数作为基准
- 将小于基准的数放在左边,不小于基准的数放在右边
- 对基准两边的部分进行快速排序
下面是 F# 实现快速排序的例子
let rec quicksort list =
match list with
| [] -> []
| firstElem::otherElements ->
// 小于第一个元素的元素
let smallerElements =
otherElements
|> List.filter (fun e -> e < firstElem)
|> quicksort // 不小于第一个元素的元素
let largerElements =
otherElements
|> List.filter (fun e -> e >= firstElem)
|> quicksort // 连接成新列表
List.concat [smallerElements; [firstElem]; largerElements] printfn "%A" (quicksort [;;;;;;])
如果应用库函数和一些技巧,代码可压缩成:
let rec quicksort = function
| [] -> []
| first::rest ->
let smaller,larger = List.partition ((>=) first) rest
List.concat [quicksort smaller; [first]; quicksort2 larger]
参考网站:http://fsharpforfunandprofit.com/