避免表示状态的可变布尔值

时间:2016-08-05 00:52:28

标签: f#

假设在您进入长时间运行的流程之前,您必须执行一些预启动检查。

假设一旦满足这些检查,您就不需要再次进行检查(可能有很多检查并且执行起来也相对昂贵)。

如何在这里避免国家?

更一般地说,在Python中,你可以使用类似于生成器或协同程序的东西来保存这些检查和状态。是否有一种很好的F-sharpy方式来摆脱可变的布尔值,以表示跳过每一个满意的支票?

let r = new System.Random()

let someCondition1 () = 
    r.Next() % 523452321 = 0 

let someCondition2 () = 
    r.Next() % 243142321 = 0 

let mutable conditionCheck1 = false
let mutable conditionCheck2 = false
let rec conditionChecks () =

    match conditionCheck1 with
    | true -> ()
    | false -> match someCondition1 () with
               | false -> conditionChecks ()
               | true  -> conditionCheck1 <- true // never check again

    match conditionCheck2 with
    | true -> ()
    | false -> match someCondition2 () with
               | false -> conditionChecks ()
               | true  -> conditionCheck2 <- true // never check again

let rec eventLoop () =
    eventLoop ()

conditionChecks ()
eventLoop ()

2 个答案:

答案 0 :(得分:6)

我看到你的conditionChecks函数在每个false值之后递归。在我看来,你正在尝试编写以下算法:

  1. 为每个要检查的条件编写一个函数。
  2. 等待所有条件成立。 (继续重新检查任何错误的情况。)
  3. 继续进行事件循环。
  4. 在我看来,每个条件检查都是某种async表达式,这将是一个很好的解决方案。 async将继续运行,直到条件成立为止,然后结束并返回true值。然后,您将在列表中收集异步,并同步运行整个asyncs列表。奖励:如果条件可以并行执行,这将节省您的时间。

    let r = new System.Random()
    
    let rec someCondition1 () =
        async {
            // if r.Next() % 523452321 = 0 then
            printfn "Checking condition 1"
            if r.Next() % 52 = 0 then  // So our output is shorter
                return true
            else
                return! someCondition1 ()
        }
    
    let rec someCondition2 () = 
        async {
            // if r.Next() % 243142321 = 0 then
            printfn "Checking condition 2"
            if r.Next() % 24 = 0 then  // So our output is shorter
                return true
            else
                return! someCondition2 ()
        }
    
    let allConditions = [
        someCondition1 ()
        someCondition2 ()
    ]
    
    let rec eventLoop () =
        printfn "Event loop runs now"
        // eventLoop ()  // Disabled so our test run will not infiloop
    
    let ready = allConditions |> Async.Parallel |> Async.RunSynchronously
    if Array.reduce (&&) ready then
        eventLoop()
    else
        printfn "Some conditions returned false somehow"
    

    当然,为我运行会产生不同的结果,但它们通常看起来像这样:

    Checking condition 1
    Checking condition 1
    Checking condition 1
    Checking condition 1
    Checking condition 1
    Checking condition 1
    Checking condition 1
    Checking condition 1
    Checking condition 1
    Checking condition 2
    Checking condition 2
    Checking condition 1
    Checking condition 2
    Checking condition 1
    Checking condition 2
    Checking condition 1
    Checking condition 1
    Checking condition 1
    Checking condition 1
    Checking condition 1
    Checking condition 1
    Checking condition 1
    Checking condition 1
    Checking condition 1
    Checking condition 1
    Checking condition 1
    Checking condition 1
    Checking condition 1
    Checking condition 1
    Event loop runs now
    

    正如您所看到的,条件2在第四次尝试时设法返回true并停止 - 而条件1在获得true结果之前需要大约二十五次尝试。但是,一旦所有结果都是true,事件循环就会运行。

    顺便说一下,我写这个的方式也允许条件检查中的“中止”。如果任何条件检查可以确定它永远不会可满足,它可以返回false,在这种情况下,事件循环将不会运行。所以:

    let condition1CanNeverBeTrue () =
        r.Next() % 123456789 = 0
    
    let rec someCondition1 () =
        async {
            if r.Next() % 523452321 = 0 then
                return true
            else
                if condition1CanNeverBeTrue() then
                    return false
                else
                    return! someCondition1 ()
        }
    

    您可能不需要这个额外的功能,但它可能会派上用场。

    此外,如果您无法并行运行条件但它们必须按顺序运行,则将let ready = ...行替换为:

    let ready = allConditions |> List.map Async.RunSynchronously
    

    当然,最后使用List.reduce代替Array.reduce

答案 1 :(得分:2)

我认为懒惰的价值在这里会很好用。

你可以让懒惰值存储一个函数的结果,然后它为你做缓存。