F# 语法概览
F#和C#的语法差别
语法上,F#和C#有两个主要差别:
- 用缩进而非花括号分隔代码块
- 用空白而非逗号分隔参数
F#常见语法元素
以下是F#代码中常见的语法元素
注释
// 这是单行注释 (* 这是多行注释 第二行 最后一行 *)
let 绑定
let myInt = 5 let myFloat = 3.14 let myString = "hello"
上面的语句没有显式指定 myInt, myFloat, myString 的类型,类型由编译器推断。
列表
let twoToFive = [2;3;4;5] // 方括号表示列表,元素用分号分隔 let oneToFive = 1 :: twoToFive // 符号 :: 将值添加到列表头部,得到新列表,结果为 [1;2;3;4;5] let zeroToFive = [0;1] @ twoToFive // 符号 @ 连接两个列表,得到新列表,结果为 [0;1;2;3;4;5]
务必注意,列表元素使用分号分隔,而非逗号分隔。
函数
命名函数用 let 关键字定义,匿名函数用 fun 关键字定义。
let square x = x * x // 使用 let 定义命名函数,函数形参不用小括号围住 square 3 // 运行函数,实参也没有小括号 let add x y = x + y // 不可使用 (x,y) add 2 3 // 运行函数 // 多行函数,用缩进,不用分号 let evens list = let isEven x = x%2 = 0 // 内部函数 List.filter isEven list // List.filter 是库函数 evens oneToFive // 运行函数 // 用小括号指明优先级 let sumOfSquaresTo100 = List.sum (List.map square [1..100]) // 如果没有小括号,那么 List.map 会是 List.sum 的参数 // 管道 |>,将操作的输出传给下一个操作 let sumOfSquaresTo100piped = [1..100] |> List.map square |> List.sum // 用 fun 关键字定义拉姆达(匿名函数) let sumOfSquaresTo100withFun = [1..100] |> 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(99) 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 = 1,2 // 二元组 let threeTuple = "a",2,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" 1 2.0 true printfn "A string %s, and something generic %A" "hello" [1;2;3;4] // 复杂类型具有内置的美观输出格式 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" 1 2.0 true let str2 = sprintf "A string %s, and something generic %A" "hello" [1;2;3;4]
比较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 = 0; for (int i = 1; i <= n; i++) { sum += Square(i); } return sum; } }
F#版
let square x = x * x let sumOfSquares n = [1..n] // 创建 1 到 n 的列表 |> List.map square // 对每个元素求平方,得到新列表 |> List.sum // 将平方列表求和
F# 语法噪音小,没有花括号,分号,并且在这里不需要显式指定类型。
C#版,使用 linq
public static class FunctionalSumOfSquaresHelper { public static int SumOfSquares(int n) { return Enumerable.Range(1, 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 [1;5;23;18;9;1;3])
如果应用库函数和一些技巧,代码可压缩成:
let rec quicksort = function | [] -> [] | first::rest -> let smaller,larger = List.partition ((>=) first) rest List.concat [quicksort smaller; [first]; quicksort2 larger]
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步