2016-08-05 35 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

我覺得懶惰的值在這裏很好用。

你可以讓懶惰的值存儲函數的結果,然後它爲你做緩存。