F#折叠三元组中的序列
我在谷歌上搜索和阅读过,我试图找到一种“正确”的方式来做,但我读到的每一个问题似乎都有完全不同的答案 这是我问题的要点。文件的类型签名为三元组中的一个序列(a:string,b:string,c:Int64)。作为f#的新手,我仍然不能流利地表达类型签名(或者理解它们)。a是文件名,b是内部标识符,c是表示文件长度(大小)的值。baseconfig是代码前面的一个字符串F#折叠三元组中的序列,f#,fold,F#,Fold,我在谷歌上搜索和阅读过,我试图找到一种“正确”的方式来做,但我读到的每一个问题似乎都有完全不同的答案 这是我问题的要点。文件的类型签名为三元组中的一个序列(a:string,b:string,c:Int64)。作为f#的新手,我仍然不能流利地表达类型签名(或者理解它们)。a是文件名,b是内部标识符,c是表示文件长度(大小)的值。baseconfig是代码前面的一个字符串 ignore(files |> Seq.filter( fun(x,y,z) -> y = basec
ignore(files
|> Seq.filter( fun(x,y,z) -> y = baseconfig) // used to filter only files we want
|> Seq.fold( fun f n ->
if( (fun (_,_,z) -> z) n > 50L*1024L*1024L) then
zipfilex.Add((fun (z:string, _, _) -> z) n)
printfn("Adding 50mb to zip")
zipfilex.CommitUpdate()
zipfilex.BeginUpdate()
("","",0L)
else
zipfilex.Add((fun (z, _, _) -> z) n)
("", "", (fun (_, _, z:Int64) -> z) n + (fun (_, _, z:Int64) -> z) f)
) ("","",0L)
)
这段代码应该做的是迭代文件中的每个文件,将其添加到zip存档(但不是真的,它只是在稍后提交的列表中),当文件超过50MB时,将当前挂起的文件提交到zip存档。添加文件成本很低,提交成本很高,因此我尝试通过批处理来降低成本
到目前为止,代码还可以工作。。。除了ObjectDisposedException,当它接近150MB的提交文件时,我得到了它。但我不确定这样做是否正确。感觉上我在以一种非传统的方式使用Seq.fold
,但是,我不知道有什么更好的方法
附加问题:有没有更好的方法从元组中删除值?fst和snd只适用于2值元组,我意识到您可以定义自己的函数,而不是像我那样内联它们,但似乎应该有更好的方法
更新:我以前在fold的尝试中,我不明白为什么我不能仅仅使用Int64作为累加器。结果我遗漏了一些关键的括号。下面是一个简单的版本。还消除了所有疯狂的元组提取
ignore(foundoldfiles
|> Seq.filter( fun (x,y,z) -> y = baseconfig)
|> Seq.fold( fun (a) (f,g,j) ->
zipfilex.Add( f)
if( a > 50L*1024L*1024L) then
printfn("Adding 50mb to zip")
zipfilex.CommitUpdate()
zipfilex.BeginUpdate()
0L
else
a + j
) 0L
)
更新2:我将不得不采用一种强制性的解决方案,在zip文件在随后的语句中关闭后,F#以某种方式重新输入这段代码。这解释了ObjectDisposedException。不知道这是怎么回事,也不知道为什么。以下是我的看法:
let inline zip a b = a, b
foundoldfiles
|> Seq.filter (fun (_, internalid, _) -> internalid = baseconfig)
|> zip 0L
||> Seq.fold (fun acc (filename, _, filesize) ->
zipfilex.Add filename
let acc = acc + filesize
if acc > 50L*1024L*1024L then
printfn "Adding 50mb to zip"
zipfilex.CommitUpdate ()
zipfilex.BeginUpdate ()
0L
else acc)
|> ignore
一些注意事项:
zip
helper函数可以在整个函数中创建一个干净的管道,而不会产生任何开销,在更复杂的场景中,由于状态从fold
functor的右侧移到左侧,因此有助于类型推断(尽管在这种特殊情况下,这并不重要,也没有帮助)
- 使用
\uu
在本地丢弃不需要的元组元素可以使代码更易于阅读
- 通过管道传输到
ignore
而不是用额外的括号包装整个表达式的方法使代码更易于阅读
- 将一元函数的参数用括号括起来看起来很奇怪;不能对非一元货币函数使用括号,因此对一元函数使用括号是不一致的。我的策略是为构造函数调用和元组函数调用保留括号
编辑:注意如果(a>50L*1024L*1024L)那么
是不正确的逻辑-如果
需要考虑累加器和当前文件大小。例如,如果第一个文件>=50MB,则不会触发if。以下是我的看法:
let inline zip a b = a, b
foundoldfiles
|> Seq.filter (fun (_, internalid, _) -> internalid = baseconfig)
|> zip 0L
||> Seq.fold (fun acc (filename, _, filesize) ->
zipfilex.Add filename
let acc = acc + filesize
if acc > 50L*1024L*1024L then
printfn "Adding 50mb to zip"
zipfilex.CommitUpdate ()
zipfilex.BeginUpdate ()
0L
else acc)
|> ignore
一些注意事项:
zip
helper函数可以在整个函数中创建一个干净的管道,而不会产生任何开销,在更复杂的场景中,由于状态从fold
functor的右侧移到左侧,因此有助于类型推断(尽管在这种特殊情况下,这并不重要,也没有帮助)
- 使用
\uu
在本地丢弃不需要的元组元素可以使代码更易于阅读
- 通过管道传输到
ignore
而不是用额外的括号包装整个表达式的方法使代码更易于阅读
- 将一元函数的参数用括号括起来看起来很奇怪;不能对非一元货币函数使用括号,因此对一元函数使用括号是不一致的。我的策略是为构造函数调用和元组函数调用保留括号
编辑:注意如果(a>50L*1024L*1024L)那么
是不正确的逻辑-如果
需要考虑累加器和当前文件大小。例如,如果第一个文件>=50MB,则不会触发if。我认为您的问题不会因为使用折叠而受益。它在构建不可变结构时最有用。在这种情况下,我的观点是,它让你试图做的事情变得不那么清晰。强制解决方案运行良好:
let mutable a = 0L
for (f, g, j) in foundoldfiles do
if g = baseconfig then
zipfilex.Add(f)
if a > 50L * 1024L * 1024L then
printfn "Adding 50mb to zip"
zipfilex.CommitUpdate()
zipfilex.BeginUpdate()
a <- 0L
else
a <- a + j
让可变a=0L
对于foundoldfiles中的(f,g,j)do
如果g=baseconfig,则
zipfilex.Add(f)
如果a>50L*1024L*1024L,则
打印fn“将50mb添加到zip”
zipfilex.CommitUpdate()
zipfilex.BeginUpdate()
a我不认为你的问题从使用fold
中获益。它在构建不可变结构时最有用。在这种情况下,我的观点是,它让你试图做的事情变得不那么清晰。强制解决方案运行良好:
let mutable a = 0L
for (f, g, j) in foundoldfiles do
if g = baseconfig then
zipfilex.Add(f)
if a > 50L * 1024L * 1024L then
printfn "Adding 50mb to zip"
zipfilex.CommitUpdate()
zipfilex.BeginUpdate()
a <- 0L
else
a <- a + j
让可变a=0L
对于foundoldfiles中的(f,g,j)do
如果g=baseconfig,则
zipfilex.Add(f)
如果a>50L*1024L*1024L,则
打印fn“将50mb添加到zip”
zipfilex.CommitUpdate()
zipfilex.BeginUpdate()
a作为“脏”命令式样式的替代,您可以使用一个通用且可重用的分块函数扩展Seq
模块。该函数有点像fold
,但它使用返回选项的lambda作为“dirty”命令式样式的替代,您可以使用用于分块的通用和可重用函数扩展Seq
模块。这个函数有点像fold
,但是如果你不喜欢mutab,它需要一个lambda返回选项
let rec loop acc = function
| (file, id, size) :: files ->
if id = baseconfig then
zipfilex.Add file
if acc > 50L*1024L*1024L then
printfn "Adding 50mb to zip"
zipfilex.CommitUpdate()
zipfilex.BeginUpdate()
loop 0L files
else
loop (acc + size) files
else
loop acc files
| [] -> ()
loop 0L foundoldfiles
let rec loop acc = function
| (file, id, size) :: files when id = baseconfig ->
zipfilex.Add file
if acc > 50L*1024L*1024L then
printfn "Adding 50mb to zip"
zipfilex.CommitUpdate()
zipfilex.BeginUpdate()
loop 0L files
else
loop (acc + size) files
| _ :: files -> loop acc files
| [] -> ()
loop 0L foundoldfiles