F# 中的协程

Posted

技术标签:

【中文标题】F# 中的协程【英文标题】:Coroutines in F# 【发布时间】:2013-11-17 14:21:46 【问题描述】:

我刚刚开始使用 F# 使用 Unity3D,我注意到 coroutines 在书籍和教程中被大量使用,作为解决各种问题的巧妙解决方案。我一直在试图弄清楚 F# 是否具有等效的内置结构,或者是否至少可以以某种方式模仿它们,但我在 MSDN 上找不到任何东西。我只找到了几篇关于使用 Continuation monad 实现协程的文章,但对于初学者来说,这些都超出了我的想象。

以下是 Unity 文档中的 C# 示例,当在游戏循环中重复调用时,会导致对象的 alpha 颜色随着时间的推移以小幅递增:

IEnumerator Fade() 
    for (float f = 1f; f >= 0; f -= 0.1f) 
        Color c = renderer.material.color;
        c.a = f;
        renderer.material.color = c;
        yield return;
    

所以我只需要声明一个返回 IEnumerator 的函数,然后通过“yield”将控制权交给我想要在主体内部的任何位置。我不确定如何在 F# 中执行此操作,因为我不断收到错误消息“此表达式应具有 IEnumerator 类型,但此处具有单元类型”。 “yield”关键字在 F# 中的行为似乎也有所不同,因为与 C# 不同,它不能单独使用,并且必须在我从文档中理解的序列表达式中。

所以我错过了什么吗?上面的功能如何在 F# 中实现?


更新

古斯塔沃的解释是正确的。这是您可以附加到对象以查看其红色值在 10 秒时间范围内减少 0.1 的确切 Unity 脚本。

namespace CoroutinesExample
open UnityEngine

type CoroutinesExample() =
    inherit MonoBehaviour()

    member this.Start() =
        // Either of the these two calls will work
        // this.StartCoroutine("Fade") |> ignore
        this.Fade()                       

    member this.Fade() =
       seq  
           for f in 1.f .. -0.1f .. 0.f do 
               let mutable c = this.renderer.material.color
               c.r <- f 
               this.renderer.material.color <- c
               yield WaitForSeconds(1.f) 
        :?> IEnumerator

This article 非常有助于解释 Unity 中协程的细节。

【问题讨论】:

Unity/C# 中的协程只是 IEnumerator 的。尝试使用您的“seq”关键字:msdn.microsoft.com/en-us/library/dd233209.aspx IEnumerable 和 IEnumerator 是一样的吗? F# 序列的文档提到它们是“IEnumerable(T) 的别名”,而 Unity 希望我有一个具有 IEnumerator 返回类型的函数。无论如何,我正在尝试按照您的建议使用 seq ,但我认为我做得不对。你介意看看我的代码吗?我已经把它放在这里了pastebin.com/Z0GE2Vy1 IEnumerable 包含一个 IEnumerator。如果你只需要后一个,你可以从 seq [=IEnumerable] 中检索它 实际上,他们只需要一个 IEnumerator 是有道理的,因为如果我理解的话,他们关心的唯一操作是“前进一点”,而实际上并不关心该操作的返回值。所以如果你让 x = seq 然后 x.GetEnumerator() 你应该很好地按照这个猜测去 @nicolas 是的,我认为这是我从阅读 [这篇文章] (altdevblogaday.com/2011/07/07/unity3d-coroutines-in-detail) 中得到的要点,尽管我仍在掌握 .net,所以我不确定我是否正确理解 IEnumerable/IEnumerator 部分。但是,我确实通过 member this.Fade():IEnumerator = x.GetEnumerator() 尝试了您的建议,但我现在收到错误消息“表达式应具有 IEnumerator 类型,但此处具有 Generic.IEnumerator 类型” 【参考方案1】:

等效的 F# 代码如下:

member this.Fade() =
    seq 
        for f in 1.0 .. -0.1 .. 0.0 do
            let c = renderer.material.color
            c.alpha <- f
            renderer.material.color <- c
            yield ()
     :> IEnumerable

请注意,与 C# 不同,您必须产生一些值,因此我们使用单位 (())。 seq 表达式的类型为seq&lt;unit&gt;,它是IEnumerable&lt;Unit&gt; 的别名。为了使其符合 Unity 期望的类型,我们只需使用 :&gt; IEnumerable 向上转换它

【讨论】:

这似乎不起作用,因为 Unity 引擎正在寻找一个具有 IEnumerator 返回类型的函数,所以我必须像这样声明淡入淡出:member this.Fade():IEnumerator 谢谢!这几乎可以工作了。在member this.Fade() = x.GetEnumerator() :&gt; IEnumerator 中进行向上转换是可行的,但如果我像你一样在内联进行,我实际上必须用:?&gt; IEnumerator 向下转换它才能工作。我不确定为什么。我会更新我的代码。 还有一点,演员必须是 IEnumerator,而不是 IEnumerable

以上是关于F# 中的协程的主要内容,如果未能解决你的问题,请参考以下文章

你应该知道的协程中的挂起转化小技巧

F# 中的协程

Unity优化如何实现Unity编辑器中的协程

Kotlin 协程Flow 异步流 ⑤ ( 流的上下文 | 上下文保存 | 查看流发射和收集的协程 | 不能在不同协程中执行流的发射和收集操作 | 修改流发射的协程上下文 | flowOn函数 )

Kotlin 协程Flow 异步流 ⑤ ( 流的上下文 | 上下文保存 | 查看流发射和收集的协程 | 不能在不同协程中执行流的发射和收集操作 | 修改流发射的协程上下文 | flowOn函数 )

Lua中的协程coroutine