Forward Pipe Operator(|>)
|>操作符只是简单地定义为:
其类型前面信息为:
可以这么来理解:x的类型为'a,函数f接受'a类型的参数,返回类型为'b,操作符的结果就是将x传递给f后所求得的值。
还是来看个例子吧:
let square x = x * x
let toStr (x : int) = x.ToString()
let rev (x : string) = new String(Array.rev (x.ToCharArray()))
// 32 -> 1024 -> "1024" -> "4201"
let result = rev (toStr (square 32))
上面的代码是很直白的,但语法看起来却不太好。我们所做的就是将一个运算的结果传给下一个运算。我们可以通过引入几个变量来改写代码为:
let step2 = toStr step1
let step3 = rev step2
let result = step3
但是我们需要维护这几个临时变量。|>操作符接受一个值,将其“转交”给一个函数。这会大大地简化F#代码:
序列(Sequence,System.Collections.Generic.IEnumerator<_>)
序列(在F#中为seq)是 System.Collections.Generic.IEnumerator的别名,但它在F#中有另外的作用。不像列表和数组,序列可包含无穷个值。只有当前的值保存在内存中,一旦序列计算了下个值,当前的值就会被忘记(丢弃)。例如,下面的代码生成了一个包含所有整数的序列。
集合(Collections:Seq,List,Array)
在F#中,如果您想表示一个值的集合,至少有三个好的选择——数组、列表和序列,它们都有各自的优点。而且每种类型都有一系列的模块内置于F#库中。您可以使用VS的智能感知来探究这些方法,这里我们来看看最常用的那些:
iter。“iter”函数遍历集合的每一项。这与“foreach”循环是一致的。下面的代码打印列表的每一项:
map。像我在上篇文章中所说的,map函数基于一个指定的函数对集合的值进行转换。下面的例子将数组的整数值转换为它们的字符串表示:
fold。“fold”函数接受一个集合,并将集合的值折叠为单个的值。像iter和map一样,它接受一个函数,将其应用于集合的每个元素,但它还接受另一个“accumulator”参数。fold函数基于上一次运算不断地累积accumulator参数的值。看下面的例子:
该代码的功能是:以10为基数(acculator),累加序列中的每一项。
只有序列有fold方法,列表和数组则有fold_left和fold_right方法。它们的不同之处在于计算顺序的不同。
可选值(Option Values)
基于函数式编程的特点,在F#中很难见到null值。但有些情况下,null值比未初始化变量更有意义。有时可选值则表示值未提供(可选值就像C#中的nullable类型)。
F#中的“可选类型(option type)”有两种状态:“Some”和“None”。在下面的记录类型Person中,中间的字段可能有值,也可能没有值。
let billg = {First = "Bill"; MI = Some("H"); Last = "Gates" }
let chrsmith = {First = "Chris"; MI = None; Last = "Smith" }
延迟求值(惰性值,Lazy Values,Microsoft.FSharp.Core.Lazy<_>)
延迟初始化表示一些值,它们在需要时才进行计算。F#拥有延迟求值特性。看下面的例子,“x”是一个整数,当对其进行求值时会打印“Computed”。
val x : Lazy<int>
> let listOfX = [x; x; x];;
val listOfX : Lazy<int> list
> x.Force();;
Computed.
val it : int = 42
可以看到,我们在调用“Force”方法时,对x进行求值,返回的值是42。您可以使用延迟初始化来避免不必要的计算。另外在构造递归值时,也很有用。例如,考虑一个Union值,它用来表示循环列表:
| ListNode of int * InfiniteList
let rec circularList = ListNode(1, circularList)
“circularList”拥有对自身的引用(表示一个无限循环)。不使用延迟初始化的话,声明这样类型的值是不可能的。
现在,应该对F#语言有了一定的足够的了解。在后续文章中,回陆续介绍一些F#的使用技巧。