Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/jquery-ui/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F# 生命游戏的递归函数不会异步运行_F# - Fatal编程技术网

F# 生命游戏的递归函数不会异步运行

F# 生命游戏的递归函数不会异步运行,f#,F#,我正在尝试运行一个函数来更新一个网格,用于一个用f#编写的生命游戏函数,所有东西都必须是递归的,没有可变项。我想通过异步运行Update函数在表单中添加一个暂停按钮,但是当我这样做时,只有一个方块被更新。但是,当我在没有异步的情况下单步执行程序时,所有的方块都会更新。你知道为什么吗 let buttonGrid : Button list list = (Startup ar);; //transform buttongrid to int grid let rec bg2ig (bg:But

我正在尝试运行一个函数来更新一个网格,用于一个用f#编写的生命游戏函数,所有东西都必须是递归的,没有可变项。我想通过异步运行Update函数在表单中添加一个暂停按钮,但是当我这样做时,只有一个方块被更新。但是,当我在没有异步的情况下单步执行程序时,所有的方块都会更新。你知道为什么吗

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
let buttonGrid:Button list=(启动ar);;
//将buttongrid转换为int grid
让rec bg2ig(bg:按钮列表)=
let rec innerLoop(bl:按钮列表)=
匹配bl
|[] -> []
|x::xs->if(x.Name=“0”)则0:(innerLoop xs)否则1:(innerLoop xs)
将bg与
|[] -> []
|y::ys->(内部循环y):(bg2ig ys)
让我们更新(背景:按钮列表)=
设ar=(bg2ig-bg)
let rec innerUpdate(背景:按钮列表)=
let rec arrayLoop(bl:按钮列表)y=
匹配bl
|[] -> 0
|x::xs->
设X=(15 X.长度)
设n=(邻域X y ar)
如果(ar.[X].[y]=0),则(如果n=3,则按buttonGrid.[X].[y]。名称忽略

如注释中所述,
异步.RunSynchronously
对于此场景是一个错误的函数。它在后台线程上启动工作流(这是错误的,因为您想访问GUI元素),然后它会阻止调用线程,直到后台工作完成(这是错误的,因为您不想阻止GUI线程)

您需要使用
Async.startimimediate
在当前线程(即GUI线程)上启动工作而不阻塞。当工作流的第一部分完成时(在
Sleep
之前),GUI线程可以自由执行其他工作。在
Sleep
之后,工作流将再次在GUI线程上继续(这是通过
startimimediate
自动完成的),因此您可以再次访问GUI

另外,执行实际循环的
子运行
函数也需要是异步的-因此我希望循环的主要部分如下所示:

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

如注释中所述,
Async.RunSynchronously
对于这种情况是一个错误的函数。它在后台线程上启动工作流(这是错误的,因为您想访问GUI元素),然后它会阻止调用线程,直到后台工作完成(这是错误的,因为您不想阻止GUI线程)

您需要使用
Async.startimimediate
在当前线程(即GUI线程)上启动工作而不阻塞。当工作流的第一部分完成时(在
Sleep
之前),GUI线程可以自由执行其他工作。在
Sleep
之后,工作流将再次在GUI线程上继续(这是通过
startimimediate
自动完成的),因此您可以再次访问GUI

另外,执行实际循环的
子运行
函数也需要是异步的-因此我希望循环的主要部分如下所示:

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

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"))
让b=新按钮(位置=新点(50500),文本=(“运行”),宽度=100,高度=40)
让btnPause=new按钮(位置=新点(150500),Text=“停止”,宽度=100,高度=40,启用=false)
b、 单击。添加(乐趣->
让cancellationSource=新的CancellationTokenSource()

b、 启用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"))
让b=新按钮(位置=新点(50500),文本=(“运行”),宽度=100,高度=40)
让btnPause=new按钮(位置=新点(150500),Text=“停止”,宽度=100,高度=40,启用=false)
b、 单击。添加(乐趣->
让cancellationSource=新的CancellationTokenSource()

b、 启用
子运行
函数有些奇怪。它看起来永远不会终止(当
x
始终为1时)。是的,我注意到,我以前在那里睡觉过,所以它会在执行更新之前等待1秒。我循环的原因是为了让我的生命游戏继续运行。我想我可以使用取消令牌来停止递归循环,但我无法理解为什么它不能正常工作。访问WinForms对象可能有问题来自非UI线程的。尝试使用
Async.startimimediate
而不是
Async.RunSynchronously
。是的,这就是问题所在
子运行
函数有些奇怪。它看起来永远不会终止(其中
x
始终为1)。是的,我注意到,我以前在那里睡觉过,所以它会在执行更新之前等待1秒。我循环的原因是为了让我的生命游戏继续运行。我想我可以使用取消令牌来停止递归循环,但我无法理解为什么它不能正常工作。访问WinForms对象可能有问题s来自非UI线程。尝试使用
Async.startimimediate
而不是
Async.RunSynchronously
。是的,这就是问题所在。谢谢,你的回答帮助med解决了我最初的问题,但最终我以不同的方式编写了函数。谢谢,你的回答帮助med解决了我最初的问题,但最终我编写了对功能进行不同的分类,哪些功能起作用