一些基本的seq和列表问题

时间:2012-10-26 10:54:22

标签: f#

  

可能重复:
  Linked list partition function and reversed results

实际上我不关心输入类型或输出类型,任何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.ofListList.toArray之间有什么区别,或者更常见的是A.ofB中的B.ofAList, Seq, Array

seq myListList.toSeq myList相同?

另一个问题是,嵌套Seq.appendSeq.concat具有相同的复杂性吗?

e.g。

  Seq.append (Seq.append (Seq.append a b) c) d // looks aweful
  Seq.concat [a;b;c;d]

3 个答案:

答案 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.ofListList.toArray在内部使用相同的功能(请参阅herehere)。

3)。我认为Seq.concat与一堆Seq.append具有相同的复杂性。在ListArray的上下文中,concatappend更有效,因为您有更多信息可以为输出预先分配空间。

答案 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,它可以有效地实现。 希望这就是你的意思:)。