【问题标题】:Modifying tuple created in lambda expression by reference通过引用修改在 lambda 表达式中创建的元组
【发布时间】:2015-10-15 10:53:31
【问题描述】:

在某个软件上测试运行后,我试图解析一个非常大的日志文本文件(100 000 行以上)。为了做到这一点,我将文件划分为 1000 行的窗口,并在我们的测试运行中为每个测试递增地读取每个窗口。当我读完这 1000 行时,为了确保我不会再解析它们,我想将一个布尔值设置为 true,这意味着我应该跳过那个窗口,因为我确信我已经完成了解析它.

这是我迄今为止想出的:

...
let windowedAMLogSeq = Seq.windowed 1000 fullAMLogSeq
                                |> Seq.map (fun (window:string[]) -> (window , false))
for category in sortedTrxFailedTests do
    for failedTest in category do
        ...
        match failedTest.StartTime with
        | Some x ->
            match failedTest.EndTime with
            | Some y ->
                let accessManagerLogLines = parseAccessManagerLogFile x y windowedAMLogSeq
                ...

let parseAccessManagerLogFile (testStartTime:DateTime) (testEndTime:DateTime) (windowedAMLogSeq:seq<string[] * bool>) =
    Seq.map (parseWindow testStartTime testEndTime) windowedAMLogSeq

let parseWindow testStartTime testEndTime (window:(string[] * bool)) =
    match (snd window) with
    | true ->
        [||]
    | false ->
        let lineArray = Array.map (fun (line:string) -> if (isBetweenStartAndEndTime testStartTime testEndTime line) then line else "") (fst window)
        let cleansedArray = Array.partition (fun (line:string) -> line <> "") lineArray
                                |> fst

        match cleansedArray.Length > 0 with
        | true ->
            ()
        | false ->
            window <- ((fst window), true)  // Problem here, not mutable or by ref
        cleansedArray

显然,这不会编译,因为window 不可变或通过引用传递。我尝试了多种使用 mutable 和 ref 或 byref 关键字的组合,但没有成功。作为 F# 的新手,我不确定要使用的正确语法。

为了通过引用直接修改window 值,或者能够通过引用window 元组中的布尔值来修改,使用什么语法?

【问题讨论】:

    标签: .net reference f# tuples pass-by-reference


    【解决方案1】:

    我的问题的解决方案是使用 ref 关键字,如下所示。另外,请注意您现在必须在元组上使用 .Value 属性。

    ...
    let windowedAMLogSeq = Seq.windowed 1000 fullAMLogSeq
                                |> Seq.map (fun (window:string[]) -> 
                                                    let mutable tuple = ref (window , false)
                                                    tuple
                                                )
    for category in sortedTrxFailedTests do
        for failedTest in category do
            ...
            match failedTest.StartTime with
            | Some x ->
                match failedTest.EndTime with
                | Some y ->
                    let accessManagerLogLines = parseAccessManagerLogFile x y windowedAMLogSeq
                    ...
    

    let parseAccessManagerLogFile (testStartTime:DateTime) (testEndTime:DateTime) (windowedAMLogSeq:seq<(string[] * bool) ref>) =
        Seq.map (parseWindow testStartTime testEndTime) windowedAMLogSeq
    

    let parseWindow testStartTime testEndTime (window:(string[] * bool) ref) =
        match (snd window.Value) with
        | true ->
            [||]
        | false ->
            let lineArray = Array.map (fun (line:string) -> if (isBetweenStartAndEndTime testStartTime testEndTime line) then line else "") (fst window.Value)
            let cleansedArray = Array.partition (fun (line:string) -> line <> "") lineArray
                                    |> fst
    
            match cleansedArray.Length > 0 with
            | true ->
                ()
            | false ->
                window.Value <- ((fst window.Value), true)
    
            cleansedArray
    

    【讨论】:

    • 请注意,使用(!) 运算符而不是.Value 更为惯用:match (snd !window) with ...
    【解决方案2】:

    您可以通过将窗口保持在具有可变字段的记录类型中来使整个事情变得更简洁:

    type LogWindow =
        {
            lines: string [] 
            mutable parsed: bool
        }
    

    你可以这样创建它:

    let windowedAMLogSeq = Seq.windowed 1000 fullAMLogSeq
                           |> Seq.map (fun window -> { lines = window; parsed = false })
    

    然后在你的parseWindow:

    window.parsed <- true
    

    但是你的代码对我来说看起来很奇怪。你真的想为你的日志创建滑动窗口,而不是把它分成块吗?因为Seq.windowed 创建的是滑动窗口。

    【讨论】:

    • 我可能误解了Seq.windowed 正在做的事情,因为我的代码无法正常工作(尽管使用您的修复程序或我的修复程序,它正在编译)。基本上我不想将整个日志文件(无论是否拆分)存储到一个数组或列表中,因为该文件可能比计算机上的可用 RAM 大。这就是我使用序列的原因,因为它们不存储在内存中,但每次计算的时间更长。这就是为什么我想将它分隔在较小的窗口中,并且有一种方法不必解析我已经通过并且知道我不会再回来的窗口。我做错了吗?
    • 然后更好地了解 Seq.windowed 正在做什么 - 它可能通过一次移动一行来从 100,000 行中创建约 99,000 个窗口。
    • 很难查看调试器中发生了什么,因为我看不到其中的序列,它总是超时。有没有办法绕过 Visual Studio 2012 中的超时?
    • 不确定您看到的是什么。但通常将 seqs 转换为数组以进行调试是值得的(更不用说仅使用庞大日志中的一个样本)。使用 F# 交互也是一个好主意。
    • 我认为您应该研究如何改为以块的形式读取文件。
    猜你喜欢
    • 1970-01-01
    • 2013-06-06
    • 2014-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多