如何附加到F#中的列表而不是前置?

时间:2013-09-17 14:29:27

标签: f#

在F#中列出一个列表有点烦人,因为一旦你完成了你必须扭转它。有没有办法直接从头开始建立一个列表?

4 个答案:

答案 0 :(得分:7)

如果您需要将元素追加到末尾,则可以使用称为DList的类型。有一个implementation available in FSharpX

但是,有一些与此相关的运行时开销(例如参见comments here),因此我认为通过预先设置然后反转来构建列表通常会更有效。在函数式编程中它也是非常标准的事情 - 它起初可能看起来有点令人困惑,但是当实现遍历列表的递归函数时它是一种非常常见的“设计模式”,所以我不会试图避免它。 / p>

答案 1 :(得分:2)

预先添加和撤消列表没有任何问题。您可以在单元素列表中使用append(@),但这是代码味道。一种可容忍的(尾递归)方法是:

let appendSingle xs x =
    [ yield! xs
      yield x ]

以上所有解决方案都有O(n)执行时间。 对于您的用例,您可以保留私有ResizeArray以避免使用反向。它很好,因为隐藏了可变性。比较这个功能

let filter f l = 
  let rec loop acc l =
    match l with 
    | [] -> List.rev acc                        
    | x::xs when f x -> loop (x::acc) xs  
    | x::xs -> loop acc xs
  loop [] l

更高效的对手

let filter f l = 
  let rec loop (acc : ResizeArray<_>) l =
    match l with 
    | [] -> Seq.toList acc                        
    | x::xs when f x -> 
        acc.Add(x)  
        loop acc xs  
    | x::xs -> loop acc xs
  loop (ResizeArray()) l

答案 2 :(得分:2)

回顾您的original code,我认为您要找的是使用List.foldBack而不是List.fold。实质上,foldBack重复应用从列表末尾开始的folder函数,而不是从列表的开头。它不如fold有效,但使用foldBack并避免撤消列表会更好。

使用foldBack,您的累积函数folder将应用于列表x0::x1::...::xlast,如下所示,folder的初始参数为init

folder x0 (folder x1 ( ... (folder xlast init) ... ) )

c.f。 fold

folder (... (folder (folder init x0) x1) ...) xlast

您的原始问题还有一些其他答案可以提供替代解决方案,但坚持使用您的代码,将foldBack替换为fold会导致第一次实施

let chunkOrig items chunkSize =
    let folder =
        fun x (result, chunk) ->
            if List.length chunk < chunkSize then
                (result, x::chunk)
            else
                (chunk::result, [x])
    let (a,b) = List.foldBack folder items ([], []) 
    b::a

这已经变得更加简单,因为所有列表反转都已消失。 似乎可以工作。

> chunkOrig [1..10] 2;;
val it : int list list = [[1; 2]; [3; 4]; [5; 6]; [7; 8]; [9; 10]]

但是当你的列表没有分成相等的块时就会出错,因为foldBack从最后一个元素开始。

> chunkOrig [1..11] 2;;
val it : int list list = [[1]; [2; 3]; [4; 5]; [6; 7]; [8; 9]; [10; 11]]

您需要做的是根据当前块中剩余的长度而不是块本身来参数化本地函数folder

let chunk items chunkSize =
    let folder =
        fun x (result, lenLeft) ->
        if lenLeft > 0 then
            match result with
               | [] -> ([[x]], lenLeft-1)
               | r0::rtail -> ((x::r0)::rtail, lenLeft-1)
        else
            ([x]::result, chunkSize-1)
    let (result, lenLeft) = List.foldBack folder items ([], (List.length items) % chunkSize) 
    result

> chunk [1..10] 2;;
val it : int list list = [[1; 2]; [3; 4]; [5; 6]; [7; 8]; [9; 10]]

> chunk [1..11] 2;;
val it : int list list = [[1; 2]; [3; 4]; [5; 6]; [7; 8]; [9; 10]; [11]]

答案 3 :(得分:2)

x附加到xs,如下所示:

xs @ [x]

请注意,这是O(n)。