实际上我不关心输入类型或输出类型,任何seq, array, list
都可以。 (它不必是通用的)目前我的代码将list
作为输入,(list * list)
作为输出
let takeWhile predicator list =
let rec takeWhileRec newList remain =
match remain with
| [] -> (newList |> List.rev, remain)
| x::xs -> if predicator x then
takeWhileRec (x::newList) xs
else
(newList |> List.rev, remain)
takeWhileRec [] list
然而,存在一个陷阱。就像我看到的那样,List.rev
是O(n ^ 2),它可能会主导整体速度?我认为它比丑陋的解决方案更慢:Seq.takeWhile
,然后是count
,然后花费tail
n次......这仍然是O(n)
(如果有一个C#List,那么我会使用它而不必反转它......)
一个附带问题,Array.ofList
和List.toArray
之间有什么区别,或者更常见的是A.ofB
中的B.ofA
和List, Seq, Array
?
seq myList
与List.toSeq myList
相同?
另一个问题是,嵌套Seq.append
与Seq.concat
具有相同的复杂性吗?
e.g。
Seq.append (Seq.append (Seq.append a b) c) d // looks aweful
Seq.concat [a;b;c;d]
答案 0 :(得分:2)
回答你的问题:
1)List.rev的时间复杂度为O(n)
,takeWhile
的最差情况复杂度也为O(n)
。因此使用List.rev
不会增加函数的复杂性。使用ResizeArray
可以帮助您避免List.rev
,但您必须容忍一些变异。
let takeWhile predicate list =
let rec loop (acc: ResizeArray<_>) rest =
match rest with
| x::xs when predicate x -> acc.Add(x); loop acc xs
| _ -> (acc |> Seq.toList, rest)
loop (ResizeArray()) list
2)没有区别。 Array.ofList
和List.toArray
在内部使用相同的功能(请参阅here和here)。
3)。我认为Seq.concat
与一堆Seq.append
具有相同的复杂性。在List
和Array
的上下文中,concat
比append
更有效,因为您有更多信息可以为输出预先分配空间。
答案 1 :(得分:2)
1)List.rev
的相关实现在编译器的local.fs
中 - 它是
// optimized mutation-based implementation. This code is only valid in fslib, where mutation of private
// tail cons cells is permitted in carefully written library code.
let rec revAcc xs acc =
match xs with
| [] -> acc
| h::t -> revAcc t (h::acc)
let rev xs =
match xs with
| [] -> xs
| [_] -> xs
| h1::h2::t -> revAcc t [h2;h1]
评论看起来很奇怪,因为没有明显的突变。请注意,这实际上是O(n)
而不是O(n^2)
2)正如垫所说没有区别 - 我更喜欢使用我认为的to..
A
|> List.map ...
|> List.toArray
看起来比
更好A
|> List.map ...
|> Array.ofList
但那只是我。
3)
追加(编译器源):
[<CompiledName("Append")>]
let append (source1: seq<'T>) (source2: seq<'T>) =
checkNonNull "source1" source1
checkNonNull "source2" source2
fromGenerator(fun () -> Generator.bindG (toGenerator source1) (fun () -> toGenerator source2))
请注意,对于每个追加,我们会得到一个必须经过的额外生成器。相比之下,concat实现只有一个额外的函数而不是n
,所以使用concat
可能更好。
答案 2 :(得分:1)
怎么样:
let takeWhile pred =
let cont = ref true
List.partition (pred >> fun r -> !cont && (cont := r; r))
它使用单个库函数List.partition,它可以有效地实现。 希望这就是你的意思:)。