在 F# 中添加列表有点烦人,因为一旦完成,您必须将其反转。有没有办法从一开始就直接建立一个列表?
问问题
2837 次
4 回答
3
回顾您的原始代码,我认为您正在寻找的是使用List.foldBack
而不是List.fold
. 本质上,从列表的末尾而不是从列表的开头foldBack
重复应用函数。folder
它没有效率那么高,fold
但最好使用foldBack
并避免颠倒列表。
使用foldBack
,您的累积函数将按如下folder
方式应用于列表,其中 的初始参数是:x0::x1::...::xlast
folder
init
folder x0 (folder x1 ( ... (folder xlast init) ... ) )
参考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]]
于 2013-09-17T15:21:13.167 回答
3
x
像这样追加xs
:
xs @ [x]
请注意,这是 O(n)。
于 2013-09-20T20:30:03.100 回答
3
前置和反转列表没有任何问题。您可以在单元素列表上使用 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
于 2013-09-17T14:55:26.517 回答