代码之家  ›  专栏  ›  技术社区  ›  ca9163d9

中断迭代并获得值和状态?

f#
  •  1
  • ca9163d9  · 技术社区  · 7 年前

    我需要对列表中的每一项调用一个函数;如果函数返回-1,则立即退出。我需要返回函数结果和字符串“Done”或“Error”的总和。

    let input = seq { 0..4 } // fake input
    
    let calc1 x = // mimic failing after input 3. It's a very expensive function and should stop running after failing
        if x >= 3 then -1 else x * 2
    
    let run input calc = 
        input 
        |> Seq.map(fun x -> 
            let v = calc x
            if v = -1 then .... // Error occurred, stop the execution if gets -1. Calc will not work anymore
            v)
         |> Seq.sum, if hasError then "Error" else "Done"  
    
    run input calc // should return (6, "Error")
    run input id   // should return (20, "Done")
    
    3 回复  |  直到 7 年前
        1
  •  4
  •   Fyodor Soikin    7 年前

    有效地

    let run input calc =
        let rec inner unprocessed sum =
            match unprocessed with
            | [] -> (sum, "Done")
            | x::xs -> let res = calc x
                       if res < 0 then (sum, "Error") else inner xs (sum + res)
        inner (input |> Seq.toList) 0
    

    那么 run (seq {0..4}) (fun x -> if x >=3 then -1 else x * 2) 退货 (6,"Error") run (seq [0;1;2;1;0;0;1;1;2;2]) (fun x -> if x >=3 then -1 else x * 2) 退货 (20, "Done")

        2
  •  2
  •   Jarak    7 年前

    let run input calc = 
        let inputList = Seq.toList input
        let rec subrun inp acc = 
            match inp with
            | [] -> (acc, "Done")
            | (x :: xs) -> 
                let res = calc x
                match res with
                | Some(y) -> subrun xs (acc + y)
                | None -> (acc, "Error")
        subrun inputList 0
    

    请注意,下面的这个函数非常慢,可能是因为它使用Seq.tail(我原以为与List.tail相同)。我把它留给后人。

    let run input calc = 
        let rec subrun inp acc = 
            if Seq.isEmpty inp then
                (acc, "Done")
            else
                let res = calc (Seq.head inp)
                match res with
                | Some(x) -> subrun (Seq.tail inp) (acc + x)
                | None -> (acc, "Error")
        subrun input 0
    

    我不能百分之百肯定那会有多有效。根据我的经验,有时出于某种原因,我自己的尾部递归函数似乎比使用内置的高阶函数慢得多。这至少能让你得到正确的结果。


    下面这句话,虽然显然没有回答实际问题,但只是为了以防万一。

    let calc1 x = if x = 3 then None else Some(x*2)
    

    然后把它映射到你的输入。之后,您可以很容易地执行以下操作

    |> Seq.exists Option.isNone
    

    以查看结果seq中是否有none(如果希望得到相反的结果,可以通过管道将其连接到not)。

    Seq.choose id
    

    这将消除所有的无,而留下完整的选项。

    对于列表的求和,假设你曾经选择只剩下Somes,那么你可以这样做

    Seq.sumBy Option.get
    
        3
  •  0
  •   AMieres    7 年前

    这里有一种使用 Result

    首先我们创建函数 calcR 如果 calc Error 否则返回 Ok

    let calcR f x = 
        let r = f x
        if  r = -1 then Error "result was = -1" else
        Ok  r
    

    然后,我们创建函数 sumWhileOk Seq.fold 在输入上,只要结果是 好 啊 .

    let sumWhileOk fR = 
        Seq.fold(fun totalR v -> 
            totalR 
            |> Result.bind(fun total -> 
                fR v 
                |> Result.map      (fun r -> total + r) 
                |> Result.mapError (fun _ -> total    )
            ) 
        ) (Ok 0)
    

    Result.bind Result.map 好 啊 如果是的话 错误 它被绕过了。 Result.mapError 以当前总数作为错误。

    input |> sumWhileOk (calcR id)
    // returns: Ok 10
    
    input |> sumWhileOk (calcR calc1)
    // return:  Error 6