此代码将一个列表分成两部分,该谓词采用列表并在拆分时返回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#中有人知道更优化和更简单的方法吗?
答案 0 :(得分: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]
答案 1 :(得分: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]
答案 2 :(得分: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