2

f#で記述されたゲームオブライフ関数のグリッドを更新する関数を実行しようとしていますが、すべてが再帰的で、可変ではない必要があります。更新機能を非同期で実行してフォームに一時停止ボタンを追加したいのですが、これを実行すると、1つの正方形のみが更新されます。ただし、非同期なしでプログラムをステップ実行すると、すべての正方形が更新されます。なぜ何かアイデアはありますか?

let buttonGrid : Button list list = (Startup ar);;

//transform buttongrid to int grid
let rec bg2ig (bg:Button list list) = 
    let rec innerLoop (bl:Button list) =
        match bl with
        |[] -> []
        |x::xs -> if (x.Name = "0") then 0::(innerLoop xs) else 1::(innerLoop xs)
    match bg with
    |[] -> []
    |y::ys -> (innerLoop y)::(bg2ig ys)

let Update (bg:Button list list)=
    let ar = (bg2ig bg)
    let rec innerUpdate (bg:Button list list)= 
        let rec arrayLoop (bl:Button list) y = 
            match bl with
            |[] -> 0
            |x::xs -> 
                let X = (15-xs.Length)
                let n = (neighbors X y ar)
                if  (ar.[X].[y] = 0) then (if n=3 then buttonGrid.[X].[y].Name<-"1") else (if (n=2||n=3)=false then buttonGrid.[X].[y].Name<-"0")
                if buttonGrid.[15-xs.Length].[y].Name="0" 
                then buttonGrid.[15-xs.Length].[y].BackColor <- Color.White 
                else buttonGrid.[15-xs.Length].[y].BackColor <- Color.Black
                arrayLoop xs y
        match bg with
        |[] -> []
        |y::ys -> 
            ignore (arrayLoop y (15-ys.Length)) 
            innerUpdate ys
    innerUpdate bg

let Running = async {
    let rec SubRun (x:int) =
        ignore (Update buttonGrid)
        if x = 1 then
            SubRun 1
        else
            0
    ignore (SubRun 1)
    do! Async.Sleep(1000)
    }

let RunAll() = 
    Running
    |> Async.RunSynchronously
    |> ignore
4

2 に答える 2

4

コメントで述べたように、Async.RunSynchronouslyはこのシナリオでは間違った関数です。バックグラウンドスレッドでワークフローを開始し(GUI要素にアクセスするため間違っています)、バックグラウンド作業が完了するまで呼び出し元のスレッドをブロックします(GUIスレッドをブロックしたくないため間違っています)。

ブロックせずに現在のスレッドAsync.StartImmediate(GUIスレッドになります)で作業を開始するを使用する必要があります。ワークフローの最初の部分が完了すると(前に)、GUIスレッドは他の作業を自由に行うことができます。ワークフローが再びGUIスレッドで続行された後(これはのおかげで自動的に実行されます)、GUIに再びアクセスできるようになります。SleepSleepStartImmediate

余談SubRunですが、実際のループを実行する関数も非同期である必要があります。したがって、ループの主要部分は次のようになると思います。

let Running = async {
    let rec SubRun (x:int) = 
        // Perform update and then sleep before recursive call
        ignore (Update buttonGrid)
        do! Async.Sleep(1000)
        if x = 1 then
            return! SubRun 1
        else
            return 0 }

    // Start the loop and asynchronously ignore the result
    SubRun 1 |> Async.Ignore

let RunAll() = 
    // Start the computation immediately on the current threada
    Running |> Async.StartImmediate
于 2012-12-31T00:51:47.767 に答える
0

Tomas Petricekは私が抱えていた最初の問題を解決しましたが、物事を正しくするために、私はそれを別の方法で解決することになりました。私の最初の問題は、フォームが正しく更新されていないか、まったく更新されていないことが原因である可能性があり、非常に間違っているように見えました。

私はこのような非同期関数を書くことになりました

let rec async1(syncContext, form : System.Windows.Forms.Form, cancellationSource:CancellationTokenSource, (limit:int)) =
    async {
        do! Async.SwitchToContext(syncContext)
        ignore (Update buttonGrid)

        do! Async.SwitchToThreadPool()

        do! Async.Sleep(300)
        if limit > 1 then
            ignore (Async.Start (async1(syncContext, form, cancellationSource, (limit-1)),cancellationSource.Token))
        else if limit = -1 then
            ignore (Async.Start (async1(syncContext, form, cancellationSource, limit),cancellationSource.Token))
    }

スタートボタンとストップボタンでこのように呼び出すことができます

let b = new Button(Location=new Point(50,500), Text=("Run"), Width=100, Height=40)
let btnPause = new Button(Location=new Point(150, 500), Text="Stop", Width=100, Height=40, Enabled=false)
b.Click.Add(fun _ -> 
    let cancellationSource = new CancellationTokenSource()
    b.Enabled <- false
    btnPause.Enabled <- true
    btnSave.Enabled <- false
    btnLoad.Enabled <- false
    btnStep.Enabled <- false
    inputBox.Enabled <- false
    btnPause.Click.Add(fun _ -> 
        b.Enabled <- true
        btnPause.Enabled <- false
        btnSave.Enabled <- true
        btnLoad.Enabled <- true
        btnStep.Enabled <- true
        inputBox.Enabled <- true
        cancellationSource.Cancel())
    ignore (Async.Start (async1(syncContext, form, cancellationSource, (int inputBox.Text)),cancellationSource.Token))
    ignore (inputBox.Text <- "0"))

また、プログラムをステップスルーするためのステップボタンと、キャンセルトークンが呼び出されるまで、またはn回実行してから停止するまで、プログラムを無限に実行できる入力ボックスを追加しました。

于 2012-12-31T18:01:19.137 に答える