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

如何在F#中附加到列表,而不是前置?,f#,F#,在F#中预编一个列表有点烦人,因为一旦你完成了,你就必须把它颠倒过来。有没有一种方法可以直接从一开始就建立一个列表?如果您需要在末尾附加元素,可以使用称为DList的类型。有一个问题 但是,与此相关的运行时开销(例如,请参阅)较多,因此我认为,通过预先结束然后反转来构建列表通常会更有效。在函数式编程中,这也是一件非常标准的事情——一开始看起来可能有点混乱,但在实现遍历列表的递归函数时,这是一种非常常见的“设计模式”,因此我不会试图避免它。在列表前加上前缀和反转并没有什么错。您可以在单个元素列表上

在F#中预编一个列表有点烦人,因为一旦你完成了,你就必须把它颠倒过来。有没有一种方法可以直接从一开始就建立一个列表?

如果您需要在末尾附加元素,可以使用称为
DList的类型。有一个问题


但是,与此相关的运行时开销(例如,请参阅)较多,因此我认为,通过预先结束然后反转来构建列表通常会更有效。在函数式编程中,这也是一件非常标准的事情——一开始看起来可能有点混乱,但在实现遍历列表的递归函数时,这是一种非常常见的“设计模式”,因此我不会试图避免它。

在列表前加上前缀和反转并没有什么错。您可以在单个元素列表上使用append(
@
),但它是一种代码气味。可容忍(尾部递归)方法是:

let appendSingle xs x =
    [ yield! xs
      yield x ]
上述所有解决方案都有O(n)个执行时间。 对于您的用例,您可以保持私有状态,以避免使用反向。这很好,因为易变性是隐藏的。比较此函数

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
let filter f l=
让rec循环(acc:ResizeArray)l=
匹配
|[]->以下为收费表附件
|当f x->
附件增补(x)
环路acc xs
|x::xs->循环acc xs
循环(ResizeArray())l
回到您的,我认为您想要的是使用,而不是。本质上,
foldBack
从列表的末尾而不是从列表的开头重复应用
folder
功能。它不如
折叠
有效,但最好使用
折叠
并避免颠倒列表

使用
foldBack
,累积函数
文件夹
应用于列表
x0::x1::::::xlast
,如下所示,其中
文件夹
的初始参数为
init

folder x0 (folder x1 ( ... (folder xlast init) ... ) )
c、 f.
折叠

folder (... (folder (folder init x0) x1) ...) xlast
对于您的原始问题,还有一些其他答案建议了其他解决方案,但坚持您的代码,将
折叠
替换为
折叠
会导致第一次实现

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
但是,如果列表没有分成相等的块,则会出错,因为
foldBack
从最后一个元素开始

> chunkOrig [1..11] 2;;
val it : int list list = [[1]; [2; 3]; [4; 5]; [6; 7]; [8; 9]; [10; 11]]
您需要做的是根据当前块中剩余的长度而不是块本身来参数化本地函数
文件夹

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]]

x
附加到
xs
如下:

xs @ [x]

请注意,这是O(n)。

了解您尝试执行的操作的上下文可能会有所帮助。例如:为什么要将append(@)与单个元素列表一起使用以显示代码味道?这是一个非常有效的方法。你是
appendSingle
函数基本上与xs@[x]相同,性能特征相同,但更复杂的是,我应该说两者都是单向的,第二个是可以容忍的,因为尾部递归性。也许f#有类似Haskell的数据列表,所以你可以得到O(1)appends?我确信有一些库实现了带有append和cateable列表的列表,但在现实世界中基本上没有这种需求
xs @ [x]