F#柯里化和递归
柯里化和局部函数应用看上去也许并没有那么强大,但他们能提高我们代码的质量,也就是说看上去将更优雅。考虑一下printf函数,如果想只使用局部函数提供一个参数"%d",应该怎么做呢,看下面的例子:
这个例子展示了如何使printf局部版本来代替一个lambda表达式:
>//没有柯里化,使用lambda表达式
List.iter (fun i -> printfn "%d" i) [1 .. 3];;
1
2
3
val it : unit = ()
>//使用printfn 柯里化
List.iter (printfn "%d") [1 .. 3];;
1
2
3
val it : unit = ()
这个例子我们让我们看到了如何充分利用局部函数应用。
函数返回函数
我们都知道,函数式编程像处理数据一个处理函数,也就是函数可以作为另外一个函数的返回值。关于这一点,当我们用值的角度去考虑的时候会有一些有趣的结果。
例如下面我们定义一个函数PowerFunc,让我们看看是怎么回事:
>let PowerFunc temp = (fun i -> temp ** i);;//求几的几次方函数
val PowerFunc : float -> float -> float
>let power1 = PowerFunc 2.0;; //调用PowerFunc返回一个函数
val power1 : (float -> float)
>power1 3.0;; //求3的2次方
val it : float = 8.0;;
>let power2 = PowerFunc 3.0;; //调用PowerFunc返回一个函数
val power2 : (float -> float)
>power2 2.0;; //求3的2次方
val it : float = 9.0;;
如果仔细看PowerFunc函数,会注意到他的temp参数是用了两个lambdas的返回值,也就是temp ** i。当PowerFunc最初把2.0作为参数时,那么2.0是存储在哪儿?这个有意思的特性我们在这里叫做团包。这里先不涉及它,或者它是如何工作的。只要知道如果一个值在作用域内,它就可以被使用并可以被函数返回。以后的章节会详细讲解这个闭包。
函数的递归
一个函数能够调用他自身我们称作递归,并函数式编程里,这是非常有用的。
定义一个递归函数,我们需要有一个rec关键字。下面的代码片段字义了一个计算阶乘的函数。
>//定义一个递归函数
let rec factorial x =
if x <= 1 then
1
else
x * factorial ( x - 1);;
val factorial : int -> int
>//调用
factorial 5;;
val it : int = 120
这个rec关键字可能会引起我们的注意,因为其它语言没有这样的关键字来明确表示这是一个递归函数。rec关键字的实际用途是告诉类型推断系统,允许函数被当作类型推断进程的一部分。rec允许我们在类型推断系统确定函数类型之前调用此函数。
使用递归和高阶函数,我们能够在不需要改变值的情况下,可以很容易的实现命令式语言的排序,循环等等这些设计。下面的例子创建了一个普通函数的for和while循环/
>//for 循环函数
let rec forLoop body times =
if times <=0 then
()
else
body()
forLoop body (times - 1)
//while loop
let rec whileLoop predicate body =
if predicate() then
body()
whileLoop predicate body
else
();;
val forLoop : (unit -> unit) -> int -> unit
val whileLoop : (unit -> bool) -> (unit -> unit) -> unit
> forLoop (fun () -> printfn "Looping...") 3;;//调用for loop
Looping...
Looping...
Looping...
val it : unit = ()
>open System
whileLoop
(fun () -> DateTime.Now.DayOfWeek <> DayOfWeek.Saturday)
(fun () -> printfn "I wish it were the weekend...");;
I wish it were the weekend...
I wish it were the weekend...
val it : unit = ()
下一篇介绍相互递归