链接列表分区功能和反转结果

Rei*_*aka 7 algorithm f# list

我写了这个F#函数来将列表分区到某一点而不是更进一步 - 很像是takeWhile和之间的交叉partition.

let partitionWhile c l =
    let rec aux accl accr =
        match accr with
        | [] -> (accl, [])
        | h::t ->
            if c h then
                aux (h::accl) t
            else
                (accl, accr)
    aux [] l
Run Code Online (Sandbox Code Playgroud)

唯一的问题是"采取"的项目是相反的:

> partitionWhile ((>=) 5) [1..10];;
val it : int list * int list = ([5; 4; 3; 2; 1], [6; 7; 8; 9; 10])
Run Code Online (Sandbox Code Playgroud)

除了诉诸调用之外rev,是否有一种方法可以编写这个函数,使第一个列表的顺序正确?

Dan*_*iel 10

这是一个基于延续的版本.它是尾递归的,并按原始顺序返回列表.

let partitionWhileCps c l =
  let rec aux f = function
    | h::t when c h -> aux (fun (acc, l) -> f ((h::acc), l)) t
    | l -> f ([], l)
  aux id l
Run Code Online (Sandbox Code Playgroud)

以下是Brian的回答(以及累加器版本供参考)讨论后的一些基准测试:

let partitionWhileAcc c l =
  let rec aux acc = function
    | h::t when c h -> aux (h::acc) t
    | l -> (List.rev acc, l)
  aux [] l

let test = 
  let l = List.init 10000000 id
  (fun f ->
    let r = f ((>) 9999999) l
    printfn "%A" r)

test partitionWhileCps // Real: 00:00:06.912, CPU: 00:00:07.347, GC gen0: 78, gen1: 65, gen2: 1
test partitionWhileAcc // Real: 00:00:03.755, CPU: 00:00:03.790, GC gen0: 52, gen1: 50, gen2: 1
Run Code Online (Sandbox Code Playgroud)

Cps平均~7s,Acc~4s.简而言之,延续不需要为此练习购买任何东西.


Bri*_*ian 6

我希望你可以使用continuation,但最后调用List.rev是最好的方法.