F#程序设计-函数式编程之用函数风格来编程(2)

递归函数(Recursive Functions)

我们都知道,所谓递归函数就是函数自己调用自己,在函数式编程语言中,这是非常有用的。

在F#语言中, 定义一个递归函数,只需要在函数名前面有一个关键字rec来申明,比如下面拿一个经典的列子斐波那契数列来编写一个递归函数:

> let rec factorial x =
-     if x <= 2 then
-         1
-     else
-          factorial (x-1) +  factorial (x-2);;

val factorial : int -> int

>  factorial 3;;
val it : int = 2
>  factorial 10;;
val it : int = 55

 

在高阶函数中结合只用递归,你可以很容易地发现,在命令式编程语言中可以模拟循环结构语句到而不需要变量值。比如下面的代码中创建了两个普通的for和while循环。在这里要注意的是,在for循环中,times计数器的值是通过一个递归函数来实现的:

> let rec forLoop body times =
-     if times <= 0 then
-         ()
-     else
-         body()
-         forLoop body (times - 1);;

val forLoop : (unit -> unit) -> int -> unit

> forLoop (fun () -> printfn "Looping ...") 3;;
Looping ...
Looping ...
Looping ...
val it : unit = ()

 

> let rec whileLoop predicate body =
-     if predicate() then
-         body()
-         whileLoop predicate body
-     else
-         ();;

val whileLoop : (unit -> bool) -> (unit -> unit) -> 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...
I wish it were the weekend...

 

 

相互递归(Mutual recursion)

相互递归就是两个函数彼此相互调用对方。相互递归对于F#的类型推断来说,无疑是一个难题,因为为了确定第一个函数的类型,你需要知道第二个函数的类型, 反之亦然。

在下面的代码中,简单的编写两个函数,并且相互调用,会产生一个为定义的编译错误。从函数编程的规范来讲,在前面的函数是不能调用其后面的函数的:

> let isOdd x = if x = 1 then true else not (isEven (x -1))
- let isEven x = if x = 0 then true else not (isOdd (x-1));;

  let isOdd x = if x = 1 then true else not (isEven (x -1))
  -------------------------------------------^^^^^^

stdin(9,44): error FS0039: The value or constructor 'isEven' is not defined

 

所以,为了确定相互递归的函数能够正常工作,就必须使用关键字and,以便告诉F#编译器同时执行函数间的类型判断。下面的代码是改正后的函数:

> let rec isOdd n = (n = 1) || isEven (n - 1)
- and isEven n = (n = 0) || isOdd (n - 1);;

 

> isOdd 13;;
val it : bool = true

 

符号运算符(Symbolic Operators)

想想如果我们每次对类似1+2进行计算,不能通过+运算符而是通过自己编写的一个函数add来实现计算,那是一件多么困难的编程。幸运的是,在F#中不仅内置了像加法、减法这样的运算,还允许通过自定义运算符来实现一个更清洁、更优雅的代码。

符号运算符可以由!%&*+-./<=>?@^|~中的任意符号来表示(其中包括:只要不是第一个字符)。下面的代码定义了一个新函数!来计算数字的阶乘:

> let rec (!) x =
-     if x <= 1 then 1
-     else x * !(x-1);;

val ( ! ) : int -> int

> !5;;
val it : int = 120

 

默认情况下,当符号运算有一个以上的参数时中使用中缀表示法来表示,意思就是第一个参数在符号运算符之前,这也是使用过程中经常碰到的,比如下面的代码:

> open System.Text.RegularExpressions;;
> let (===) str (regex : string) =
-     Regex.Match(str,regex).Success;;

val ( === ) : string -> string -> bool

> "The quick brown fox" === "The (.*) fox";;
val it : bool = true

 

符号预算符在它们的参数之前,也被称作为前缀符号,必须使用~、!、?等前缀作为函数名。在下面的代码中,函数~+++以~为前缀,并且调用时,应该写成~+++ 1 2 3而不是1 ~+++ 2 3,这样是你的符号运算函数更加的适应函数编程的风格。

> let (~+++) x y z = x + y + z;;

val ( ~+++ ) : int -> int -> int -> int

> ~+++ 1 2 3;;
val it : int = 6

你可能感兴趣的:(编程,String,F#,regex,Constructor,recursion)