0

此代码通过一个谓词将一个列表分成两部分,该谓词采用一个列表并在拆分时返回 false。

let split pred ys =
    let rec split' l r = 
        match r with
        | [] -> []
        | x::xs -> if pred (x::l) then x::(split' (x::l) xs) else []
    let res = split' [] ys
    let last = ys |> Seq.skip (Seq.length res) |> Seq.toList
    (res, last)

有人知道在 F# 中更优化和更简单的方法吗?

4

3 回答 3

2

好吧,您可以使其尾递归,但随后您必须反转列表。您不想折叠它,因为它可以随时退出递归循环。我做了一些测试,并且反转列表不仅仅是尾递归所弥补的。

// val pred : ('a list -> bool)
let split pred xs =
    let rec split' l xs ys = 
        match xs with
        | [] -> [], ys
        | x::xs -> if pred (x::l) then (split' (x::l) xs (x::ys)) else x::xs, ys 
    let last, res = split' [] xs []
    (res |> List.rev, last)

一个类似于 Brian 的版本,它是尾递归的并且采用单个值谓词。

// val pred : ('a -> bool)
let split pred xs =
    let rec split' xs ys =
        match xs with
        | [] -> [], ys
        | x::xs -> if pred x then (split' xs (x::ys)) else (x::xs), ys
    let last, res = split' xs []
    (res |> List.rev, last)

这与库函数分区的不同之处在于,一旦谓词返回类似于 Seq.takeWhile 的错误类型,它就会停止获取元素。

// library function
let x, y = List.partition (fun x -> x < 5) li
printfn "%A" x  // [1; 3; 2; 4]
printfn "%A" y  // [5; 7; 6; 8]

let x, y = split (fun x -> x < 5) li
printfn "%A" x  // [1; 3]
printfn "%A" y  // [5; 7; 2; 4; 6; 8]
于 2009-11-10T23:57:48.530 回答
0

不是尾递归,而是:

let rec Break pred list =
    match list with
    | [] -> [],[]
    | x::xs when pred x -> 
        let a,b = Break pred xs
        x::a, b
    | x::xs -> [x], xs

let li = [1; 3; 5; 7; 2; 4; 6; 8]
let a, b = Break (fun x -> x < 5) li    
printfn "%A" a  // [1; 3; 5]
printfn "%A" b  // [7; 2; 4; 6; 8]

// Also note this library function
let x, y = List.partition (fun x -> x < 5) li
printfn "%A" x  // [1; 3; 2; 4]
printfn "%A" y  // [5; 7; 6; 8]
于 2009-11-11T01:15:34.827 回答
0

这是一些折叠方式:

let split' pred xs = let f (ls,rs,cond) x = if cond (ls@[x]) then (ls@[x],rs,cond) else (ls,rs@[x],(fun _->false))
                     let ls,rs,_ = List.fold f ([],[],pred) xs
                     ls, rs
于 2009-11-13T12:34:15.380 回答