柯里化、元组、函数类型、partial application等是函数编程的基础,但对那些传统的C类语法程序员来说是个新鲜的领域,本文主要参考http://blog.joycode.com/fscti/archive/2009/10/21/115745.joy的文章,简单的介绍了这一部分相关知识,如果要更加深入解这部分内容,请参看原文。
Tuples(元组)简介:
Tuples(元组)作为一个基本类型在F#中被广泛的被应用,顾名思义,一个Tuples是两个或多个值的集合,基本形式如下:
let t2 = (3, "foo") // t2 : int * string
let t3 = (4, "bar", true) // t3 : int * string * bool
其中,小括号是可选的,如下形式也合法,但不推荐
let t2 = 3, "foo"
不存在0元组和1元组
let u = () // u : unit
这里的u并不是Tuples类型,而是unit类型。(即不存在长度为0的Tuples)unit类型和C#里的void类型类似不需要返回值的函数(因为调用它们只是为了得到它们的副作用)
let x = (3) // x : int
这里的x也不是Tuples,而是原始的int型,和大多数语言一样,括号的一个主要目的仅仅是为了在你写表达式的时候需要特殊的执行顺序时重载普通操作符的顺序,像在"3*(2+1)"中。所以"(3)"仅仅是"3"的"括号版本"这里的括号只是表示强调。
对Tuples的访问
在C#中,编译器自动对元组的各元素生成了Item1、Item2这样的访问属性,在F#中,我们可以通过系统内置的fst和snd这两个函数实现对Tuples的第一个和第二个元素进行访问。
let c = fst (1, 2)
let d = snd (1, 2)
这里可能就有人要问了,如果要访问第三个的元素该怎么办呢?其实可以很简单的通过模式匹配实现这个功能。
let third (_, _, c) = c
函数类型:
在对Tuples有了一个基本的认识后,我们再来关注一下函数类型,首先看一下这个函数
// g : int * string -> unit
let g(x,s) =
printfn "%d %s" x s
g(3, "foo") // prints 3 "foo"
从g的类型可以看到,函数g接收一个类型int * string的元组,返回值为unit类型,其功能也一目了然,就是把这两个元素在屏幕上打印出来。
接下来我们再看一下这个函数
// f : int -> string -> unit
let f x s =
printfn "%d %s" x s
f 3 "foo" // prints 3 "foo"
从功能上来看,函数f和函数g实现的功能是一样的,但f参数是柯里化的,其形式为int -> string -> unit。要解释这个形式的参数,首先得把f x看成是一个函数,看做如下形式:
let f x s = …
这样,f x 就是一个接收一个int型的值,返回一个接收string型的参数并返回unit函数的函数。int -> string -> unit
如下这个例子更能说明这一点:
// fp : string -> unit
let fp = f 4
fp "bar" // prints 4 "bar"
这里只给了f一个参数并且把返回值命名为"fp"。它具有我们所期待的类型——一个接收string型的参数并返回unit函数。调用函数时所给的参数比它实际所期待的参数少(生成一个新的函数来接受剩余的参数)的这种技术叫做"partial application"。我们把"fp"叫做"f"的部分应用。它已经接受了一个int了,但是它还在等着接受一个string。这就是"柯里化"形式函数的本质。省略掉后面的参数就很容易的实现了部分应用。
部分应用也可以用在接受元组参数的函数上,不过这样做需要一个显示的lambda。
// gp : string -> unit
let gp = fun s -> g(4, s)
gp "bar" // prints 4 "bar"
以如下方式解释F#的函数可以更容易理解
-
F#函数类型被写做"A->R"
-
所有的F#函数接受一个参数。但是:
-
参数类型可以是一个元组,意思是你可以有这样的调用"g(x,s)",并且
-
返回值类型可以是另外一个函数,意思是你可以有这样的调用"f x s"。
-