## F#探险之旅（二）：函数式编程（下）

2008-09-06 11:29  Anders Cui  阅读(3679)  评论(9编辑  收藏

。有点像一连串的if...else结构，也像C++和C#中的switch，但是它更为强大和灵活。

Codelet rec luc x =    match x with    | x when x <= 0 -> failwith "value must be greater than zero"    | 1 -> 1    | 2 -> 3    | x -> luc(x - 1) + luc(x - 2)  printfn "(luc 2) = %i" (luc 2)printfn "(luc 6) = %i" (luc 6)

Output(luc 2) = 3(luc 6) = 18

Codelet boolToString x =    match x with false -> "False" | _ -> "True"

Codelet stringToBool x =    match x with    | "T" | "True" | "true" -> true    | "F" | "False" | "false" -> false    | _ -> failwith "Invalid input."

Codelet myOr b1 b2 =    match b1, b2 with    | true, _ -> true    | _, true -> true    | _ -> false   let myAnd p =    match p with    | true, true -> true    | _ -> false

Codelet listOfList = [[2; 3; 5]; [7; 11; 13]; [17; 19; 23; 29]]let rec concatenateList list =    match list with    | head :: tail -> head @ (concatenateList tail)    | [] -> []   let rec concatenateList2 list =    if List.nonempty list then        let head = List.hd list in        let tail = List.tl list in        head @ (concatenateList2 tail)    else        []       let primes = concatenateList listOfListprint_any primes

F#的类型系统提供了若干特性，可用来创建自定义类型。所有的类型可分为两类，一是元组（Tuple）或记录（Record），它们类似于C#中的类；二是Union类型，有时称为Sum类型。下面分别来看一下它们的特点。

Codelet pair = true, falselet b1, b2 = pairlet _, b3 = pairlet b4, _ = pair

Codetype Name = string// FirstName, LastNametype FullName = string * string

Codetype Organization = { Boss : string; Lackeys : string list }let family =    { Boss = "Children";     Lackeys = ["Wife"; "Husband"] }

Codetype Company = { Boss : string; Lackeys : string list }let myCom =    { new Company      with Boss = "Bill"      and Lackeys = ["Emp1"; "Emp2"] }

Codetype recipe =    { recipeName : string;      ingredients : ingredient list;      instructions : string }and ingredient =    { ingredientName : string;      quantity : int }let greenBeansPineNuts =    { recipeName = "Green Beans & Pine Nuts";      ingredients =        [{ingredientName = "Green beans"; quantity = 200};         {ingredientName = "Pine nuts"; quantity = 200}];      instructions = "Parboil the green beans for about 7 minutes." }     let name = greenBeansPineNuts.recipeNamelet toBuy =    List.fold_left        (fun acc x ->            acc + (Printf.sprintf "\t%s - %i\r\n" x.ingredientName x.quantity))        "" greenBeansPineNuts.ingredientslet instructions = greenBeansPineNuts.instructionsprintf "%s\r\n%s\r\n\r\n\t%s" name toBuy instructions

Codetype couple = { him : string; her : string }let couples =    [ { him = "Brad"; her = "Angelina" };      { him = "Becks"; her = "Posh" };      { him = "Chris"; her = "Gwyneth" } ]     let rec findDavid list =    match list with    | { him = x; her = "Posh" } :: tail -> x    | _ :: tail -> findDavid tail    | [] -> failwith "Couldn't find David"   print_string(findDavid couples)

Union类型，有时称为sum类型或discriminated union，可将一组具有不同含义或结构的数据组合在一起。可与C语言中的联合或C#中的枚举类比。先来看个例子：

Codetype Volume =| Liter of float| UsPint of float| ImperialPint of float

Volume类型属于Union类型，包含3个数据构造器（Data Constructor），每个构造器都包含单一的float值。声明其实例非常简单：

Codelet vol1 = Liter 2.5let vol2 = UsPint 2.5let vol3 = ImperialPint 2.5

Codelet convertVolumeToLiter x =    match x with    | Liter x -> x    | UsPint x -> x * 0.473    | ImperialPint x -> x * 0.568

OCaml-Styletype 'a BinaryTree =    | BinaryNode of 'a BinaryTree * 'a BinaryTree    | BinaryValue of 'a   let tree1 =    BinaryNode(        BinaryNode(BinaryValue 1, BinaryValue 2),        BinaryNode(BinaryValue 3, BinaryValue 4))

.NET-Styletype Tree<'a> =    | Node of Tree<'a> list    | Value of 'a   let tree2 =    Node( [Node([Value "One"; Value "Two"]);        Node([Value "Three"; Value "Four"])])

Codeexception SimpleExceptionexception WrongSecond of int// Hour, MInute, Secondexception WrongTime of int * int * int

Codelet testTime() =    try        let now = System.DateTime.Now in        if now.Second < 10 then            raise SimpleException        elif now.Second < 30 then            raise (WrongSecond now.Second)        elif now.Second < 50 then            raise (WrongTime (now.Hour, now.Minute, now.Second))        else            failwith "Invalid Second"    with        | SimpleException ->            printf "Simple exception"        | WrongSecond s ->            printf "Wrong second: %i" s        | WrongTime(h, m, s) ->            printf "Wrong time: %i:%i:%i" h m s        | Failure str ->            printf "Error msg: %s" str           testTime()

Codelet writeToFile() =    let file = System.IO.File.CreateText("test.txt") in    try        file.WriteLine("Hello F# Fans")    finally        file.Dispose()       writeToFile()

Codelet sixtyWithSideEffect = lazy(printfn "Hello, sixty!"; 30 + 30)print_endline "Force value the first time:"let actualValue1 = Lazy.force sixtyWithSideEffectprint_endline "Force value the second time:"let actualValue2 = Lazy.force sixtyWithSideEffect

CodeForce value the first time:Hello, sixty!Force value the second time:

《Foundations of F#》 by Robert Pickering
《Expert F#》 by Don Syme , Adam Granicz , Antonio Cisternino
F# Specs