2016-08-05 33 views
4

假设在进入长时间运行的进程之前,您必须执行一些预启动检查。避免可变的布尔值表示状态

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

这里怎么能避免状态?

更一般地说,在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() 

回答

6

我看到你的conditionChecks函数在每个false值后递归。它看起来像你试图写下面的算法:

  1. 写一个函数为每个条件进行检查。
  2. 等待所有条件成为真实。 (保持重新检查任何错误的条件)。
  3. 继续执行事件循环。

在我看来,每个条件检查是某种async表达式在这里将是一个很好的解决方案。 async将继续运行,直到条件成立,然后完成并返回值true。然后,您会收集列表中的异步,并同步运行异步的整个列表。奖励:如果条件允许他们的支票可以并行执行,这将为您节省时间。

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底。

+0

请注意在异步表达式中使用'return'和'return!'。 'return'接受给定的值并将其包装在一个'Async <'T>'(这里是一个'Async '''''),而'return!'想要一个'Async <'T>'并且按原样传递它,因此''return !someCondition1()'最终是一个尾部调用,并且这段代码永远不会耗尽堆栈 – rmunn

+0

另外请注意,当我构建'allConditions'列表时,我实际调用了'someCondition'函数,这是因为它们的签名是'单元 - > Async '。调用它们实际上并不运行异步,它只是创建'Async '对象,它不会运行,直到它传递到'Async.RunSynchronously'(或'Async.Start'或一个其他方法在'Async'模块中告诉你他们实际运行异步计算 – rmunn

+1

r.Next()调用不是线程安全的;如果你想避免同步,每个条件需要到o自己的“随机”。 – ildjarn

2

我觉得懒惰的值在这里很好用。

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