通过引用修改在 lambda 表达式中创建的元组

Modifying tuple created in lambda expression by reference

我在对某个软件进行测试后 运行 试图解析一个非常大的日志文本文件(超过 100 000 行)。为了做到这一点,我将文件分成 1000 行的 windows,并在我们的测试 运行 中为每个测试递增地读取每个 windows。当我读完这 1000 行后,为了确保我不会再无意义地解析它们,我想将布尔值设置为 true,这意味着我应该跳过 window 因为我确定我解析完毕。

这是我到目前为止想出的:

...
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 元组中的布尔值,使用什么语法?

我的问题的解决方案是使用 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

您可以通过将 window 保存在具有可变字段的记录类型中来使整个事情变得更清晰:

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

但是我觉得你的代码很奇怪。您真的要创建日志的滑动 windows,而不是将其分成块吗?因为 Seq.windowed 创建的是滑动 windows.