跳转到内容

F# 编程/解决方案/列表

来自维基教科书,开放的书籍,开放的世界
F# : 列表解决方案

配对和拆对

[编辑 | 编辑源代码]

编写两个具有以下定义的函数

val pair : 'a list -> ('a * 'a) list
val unpair : ('a * 'a) list -> 'a list

pair 函数应该将列表转换为如下所示的配对列表

pair [ 1 .. 10 ] = [(1, 2); (3, 4); (5, 6); (7, 8); (9, 10)]
pair [ "one"; "two"; "three"; "four"; "five" ] = [("one", "two"); ("three", "four")]

unpair 函数应该将配对列表转换回传统列表,如下所示

unpair [(1, 2); (3, 4); (5, 6)] =  [1; 2; 3; 4; 5; 6]
unpair [("one", "two"); ("three", "four")] = ["one"; "two"; "three"; "four"]

解决方案

[编辑 | 编辑源代码]

使用 fsi

> let pair l =
    let rec loop acc = function
        | [] | [_] -> List.rev acc
        | h1 :: h2 :: tl -> loop ((h1, h2) :: acc) tl
    loop [] l
        
let unpair l =
    let rec loop acc = function
        | [] -> List.rev acc
        | (h1, h2) :: tl -> loop (h2 :: h1 :: acc) tl
    loop [] l;;

val pair : 'a list -> ('a * 'a) list
val unpair : ('a * 'a) list -> 'a list

// Alternative implementations of unpair:
> let unpair2 l = List.foldBack (fun (x,y) s -> x::y::s) l [];;

val unpair2 : ('a * 'a) list -> 'a list

> let unpair2' xs = List.fold (fun acc x -> acc @ (fst x)::(snd x)::[]) [] xs;;

val unpair2' : ('a * 'a) list -> 'a list

> pair [ 1 .. 10 ];;
val it : (int * int) list = [(1, 2); (3, 4); (5, 6); (7, 8); (9, 10)]

> pair [ "one"; "two"; "three"; "four"; "five" ];;
val it : (string * string) list = [("one", "two"); ("three", "four")]

> unpair [(1, 2); (3, 4); (5, 6)];;
val it : int list = [1; 2; 3; 4; 5; 6]

> unpair [("one", "two"); ("three", "four")];;
val it : string list = ["one"; "two"; "three"; "four"]

> unpair (pair [ 1 .. 11 ]);;
val it : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]

> unpair2 (pair [1..11]);;
val it : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]

扩展列表

[编辑 | 编辑源代码]

编写一个具有以下类型定义的函数

val expand : 'a list -> 'a list list

expand 函数应该扩展列表,如下所示

expand [ 1 .. 5 ] = [ [1; 2; 3; 4; 5]; [2; 3; 4; 5]; [3; 4; 5]; [4; 5]; [5] ]
expand [ "monkey"; "kitty"; "bunny"; "rat" ] =
    [ ["monkey"; "kitty"; "bunny"; "rat"];
      ["kitty"; "bunny"; "rat"];
      ["bunny"; "rat"];
      ["rat"] ]

解决方案

[编辑 | 编辑源代码]

这个函数可以用或不用尾递归轻松编写。以下是在 fsi 中的两个函数

> let rec expand_not_tail_recursive = function
    | [] -> []
    | hd :: tl -> (hd :: tl) :: expand_not_tail_recursive tl
    
let expand_tail_recursive l =
    let rec loop acc = function
        | [] -> List.rev acc
        | hd :: tl -> loop ( (hd :: tl) :: acc ) tl
    loop [] l;;

val expand_not_tail_recursive : 'a list -> 'a list list
val expand_tail_recursive : 'a list -> 'a list list

> expand_tail_recursive [ 1 .. 5 ];;
val it : int list list
= [[1; 2; 3; 4; 5]; [2; 3; 4; 5]; [3; 4; 5]; [4; 5]; [5]]

> expand_tail_recursive [ "monkey"; "kitty"; "bunny"; "rat" ];;
val it : string list list
= [["monkey"; "kitty"; "bunny"; "rat"]; ["kitty"; "bunny"; "rat"];
   ["bunny"; "rat"]; ["rat"]]

> expand_not_tail_recursive [ 1 .. 5 ];;
val it : int list list
= [[1; 2; 3; 4; 5]; [2; 3; 4; 5]; [3; 4; 5]; [4; 5]; [5]]

> expand_not_tail_recursive [ "monkey"; "kitty"; "bunny"; "rat" ];;
val it : string list list
= [["monkey"; "kitty"; "bunny"; "rat"]; ["kitty"; "bunny"; "rat"];
   ["bunny"; "rat"]; ["rat"]]

列表的最大公约数

[编辑 | 编辑源代码]

gcd 可以使用欧拉算法这样实现

let rec gcd a b = if a%b=0 then b else gcd b (a%b)

gcdl 可以这样实现

let gcdl x = 
    match x with
    | [] -> 0
    | (a::r) -> List.fold gcd a x

基本归并排序

[编辑 | 编辑源代码]

split 可以这样实现

// This is the recursive split implementation
let rec split = function
| [] -> ([], [])
| [a] -> ([a], [])
| a::b::cs -> let (r,s) = split cs
              in (a::r, b::s)

// Alternatively fold can be used to implement split
// f is used to create the tupel of 2 lists. It inserts the current element in a split and changes the order of them, so the next element will be inserted in the other split.
let f (a,b) c = (b, c::a)
let split xs = List.fold f ( [], [] ) xs

merge 可以这样实现

let rec merge = function
    | ( [], ys ) -> ys
    | ( xs, [] ) -> xs
    | ( x::xr, y::yr ) -> if x<=y then x::merge( xr, y::yr ) else y::merge( x::xr, yr )
// This implementation will result in a type like this
// val merge : 'a list * 'a list -> 'a list when 'a : comparison
// The reason for this is because we're using the <= operator which can only be used on values that allow comparison (e.g. numbers).
// If you wanted to create your own datatype and implement mergesort for this datatype, you would have to use a more complex implementation

msort 可以这样实现

let rec msort = function
    | [] -> []
    | [x] -> [x]
    | xs -> let (ys, zs) = split xs
            in merge(msort ys, msort zs)
华夏公益教科书