一分钟快速上手 F# 语法

2018/09/15
原文地址: F# syntax in 60 seconds

这是对 F# 语法的一个简单的介绍,来帮助完全不会 F# 的新手阅读 F# 代码。时间紧迫,所以我在这里没法详细的介绍,不过本文的内容应该足够让你拥有理解这个系列中出现的代码样例的要点。如果你遇到了完全不能理解的代码片段也不要着急,我们在相应的地方进行详细的解释说明。那么,现在就让我们开始吧!

F# 与类 C 编程语言语法之间最大的两个区别就是:

  • 使用缩进而不是花括号来区分代码块(就像 Python 一样)

  • 使用空格而不是逗号来分隔参数

就我个人而言,当习惯了 F# 的语法后,会感觉它是非常清晰而且直白的,在许多方面,F# 的语法要比 C# 简洁的多。

下面的 F# 代码片段展示了比较常见的一些概念。

我十分希望你通过一些交互式的方式来尝试运行一下这些代码,例如:

  • 把这些代码复制到一个 F# 脚本文件中(拓展名为 .fsx 的文件),然后把它发送到交互式窗口中。见 “安装与使用 F#”

  • 或者直接在交互式窗口中输入这些代码来运行,记得要经常在代码的末尾添加 ;; 来告诉解释器你已经输入完毕,并开始执行代码。

// 像 C# 一样,可以使用两个斜杠来进行整行注释
(*
多行注释使用 (* 跟 *) 来标记
*)

// ======== "变量" (不可变的变量) ==========
// 使用 let 关键字来定义不可变的值
let myInt = 5
let myFloat = 3.14
let myString = "hello"	// F# 能够自动的推断出变量的类型

// ======== 列表 ============
let twoToFive = [2;3;4;5]        // 使用方括号跟分号来定义一个列表
let oneToFive = 1 :: twoToFive   // 使用 :: 向列表的头部添加一个新的元素来创建新的列表
// oneToFive 表示的列表是 [1;2;3;4;5]
let zeroToFive = [0;1] @ twoToFive   // 使用 @  用来连接两个列表
// 重要:F# 中从来不使用逗号作为分隔符,只使用了分号作为分隔符。

// ======== 函数 ========
// let 关键字还可以用来定义一个函数
let square x = x * x          // 注意,这里没有用的圆括号
square 3                      // 这里执行了上面的函数,还是没有使用到括号。

let add x y = x + y           // 不要通过 add (x, y) 这样的方式来调用函数,这在 F# 中有其他完全不同的含义
add 2 3                       // 现在来运行这个函数

// 如果要定义一个有多行代码体的函数,只要缩进就好了,不需要花括号,每行代码结束也不需要使用圆括号
let evens list =
   let isEven x = x%2 = 0     // 定义一个名为 isEven 局部函数
   List.filter isEven list    // List.filter 是一个库函数
                              // 他接受两个参数:一个返回布尔值的函数
                              // 和一个要进行操作的列表

evens oneToFive               // 现在来执行这个函数

// 你可以使用圆括号来说明优先级,在下面的例子中,
// 我们先对列表进行 map,然后对 map 的结果进行 sum
// 如果没有括号的话,List.map 将会被作为参数传入 List.sum
let sumOfSquaresTo100 =
   List.sum ( List.map square [1..100] )

// 你还可以使用管道运算符 |> 来把左边表达式的值传递给右边的函数作为参数
// 下面使用管道编写了一个与 sumOfSquares 相同的函数
let sumOfSquaresTo100piped =
   [1..100] |> List.map square |> List.sum  // "square" was defined earlier

// 使用 fun 关键字可以定义 Lambda(匿名函数)
let sumOfSquaresTo100withFun =
   [1..100] |> List.map (fun x->x*x) |> List.sum

// 在 F# 中,函数的返回值是隐式指定的
// 函数中最后的一个表达式所表达的值会作为函数的返回值返回出去

// ======== 模式匹配 ========
// 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"   // underscore matches anything

// Some(..) 和 None 是 F# 中类似于 Nullable 的内置类型
let validValue = Some(99)
let invalidValue = None

// 在这个例子中,我们使用了 match .. with 来对 Some 和 None 进行匹配
// 当匹配到 Some 时,它可以自动的把 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

// 记录类型(Record Type)拥有成员字段,字段间使用分号分隔
type Person = {First:string; Last:string}
let person1 = {First="john"; Last="Doe"}

// 可鉴别联合类型(Discriminated Unions)由多个子标签类型构成,标签类型之间使用 | 分隔
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 跟 Console.WriteLine
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

// F# 还提供了 sprintf 和 sprintfn 函数用来把数据格式化的输出成字符串
// 就像我们常用的 String.Format 一样

内容导航