简体   繁体   English

F#:存储和映射函数列表

[英]F#: Storing and mapping a list of functions

I have a number of events that happen in a game. 我在游戏中发生了很多事件。 I want to control the time and order at which these events occur. 我想控制这些事件发生的时间和顺序。

For example: 例如:

Event 1: Show some text on screen for N frames & play a sound effect 事件1:在屏幕上显示N帧的一些文本并播放声音效果

Event 2: Clear the text on the screen 事件2:清除屏幕上的文本

My solution (maybe there is a better one), is to have a list of functions that contain the events. 我的解决方案(可能有一个更好的解决方案)是拥有包含事件的函数列表。 The events perform their behavior then return the next events to occur in the game. 事件执行其行为然后返回在游戏中发生的下一个事件。 I thought of using List.map or List.collect because I am essentially mapping a list of events to a new list of events while performing some behavior code. 我想过使用List.map或List.collect,因为我实际上是在执行某些行为代码时将事件列表映射到新的事件列表。

In the example above, Event1 can be composed of two functions: One that shows text and one that plays a sound (hence the need for a list). 在上面的示例中,Event1可以由两个函数组成:一个显示文本,另一个播放声音(因此需要列表)。 The function that shows text would return a copy of itself for N-1 frames then it would return Event2 which clears the text. 显示文本的函数将为N-1帧返回自身的副本,然后它将返回清除文本的Event2。 The play sound function would return the equivalent of a no-op. 播放声音功能将返回相当于无操作的声音。

If this is a good solution, I could probably do it in C++ or C#. 如果这是一个很好的解决方案,我可以用C ++或C#来做。 My goal is to do an equivalent or better solution in F#. 我的目标是在F#中做同等或更好的解决方案。

Did you mean something like this? 你的意思是这样的吗?

let myActions =
    [fun () -> printfn "You've woken up a dragon."
     fun () -> printfn "You hit the dragon for 0 points of damage."
     fun () -> printfn "The dragon belches."
     fun () -> printfn "You have died."] 

let actionBuilder actionList = 
    let actions = ref actionList
    fun () ->
        match !actions with
        | [] -> ()
        | h::t -> h(); actions := t

Usage (F# interactive): 用法(F#interactive):

> let doSomething = actionBuilder myActions;;

val doSomething : (unit -> unit)

> doSomething();;
You've woken up a dragon.
val it : unit = ()
> doSomething();;
You hit the dragon for 0 points of damage.
val it : unit = ()
> doSomething();;
The dragon belches.
val it : unit = ()
> doSomething();;
You have died.
val it : unit = ()
> doSomething();;
val it : unit = ()
> 

**Edit: ** if you want to be able to add actions, maybe it's better to make an action dispenser that uses a Queue internally, since appending is O(N) with a list and O(1) with a Queue: **编辑:**如果你想能够添加动作,也许最好制作一个内部使用Queue的动作分配器,因为追加的是带有列表的O(N)和带有队列的O(1):

type actionGenerator(myActions: (unit->unit) list) =
    let Q = new System.Collections.Generic.Queue<_>(Seq.ofList myActions)

    member g.NextAction = 
        fun () -> 
            if Q.Count = 0 then ()
            else Q.Dequeue()()

    member g.AddAction(action) = Q.Enqueue(action)

Not quite sure what you're trying to achieve here... it can be helpful to think through the exact types that you're looking for. 不太确定你在这里想要实现的目标......思考你正在寻找的确切类型会很有帮助。 It sounds like perhaps you want to map a (unit->(unit->unit)) list to a (unit->unit) list by applying each function in the first. 听起来好像你想通过应用第一个中的每个函数将(unit->(unit->unit)) list映射到(unit->(unit->unit)) list (unit->unit) list If that's the case, you can do it like so: 如果是这样的话,你可以这样做:

let l = [(fun () -> (fun () -> printfn "first nested fn")); (fun () -> (fun () -> printfn "second nested fn"))]
let l' = List.map (fun f -> f()) l

If you are looking for a syntax to declare your list type then here is one way to do this: 如果您正在寻找声明列表类型的语法,那么这是一种方法:

List<`a->`b>

This assumes that the function takes a single parameter. 这假定该函数采用单个参数。

But the very fact that you are trying to figure out the syntax for the type is a hint that you are still looking at this as if you are coding in procedural language. 但是你正试图找出该类型的语法这一事实暗示你仍然在看这个就好像你是用过程语言编写的。

The "functional" way of doing it is to concentrate on the logic of generating the list and let the compiler to infer the type based on your code 这种“功能”的方式是专注于生成列表的逻辑,让编译器根据您的代码推断出类型

I've read your question twice, and am still not sure I understand exactly what you want. 我已经两次阅读你的问题了,我仍然不确定我到底知道你想要什么。 But from what I understand, your 'events' aren't necessarily invoked in the order they appear in the 'list'. 但根据我的理解,您的“事件”不一定按照它们出现在“列表”中的顺序调用。 If this is the case, you don't really want an F# list, you want some kind of lookup. 如果是这种情况,你真的不想要一个F#列表,你想要某种查找。

Now the other question is whether it is really a good idea for an event to determine what should follow it? 现在另一个问题是,一个事件确定应该遵循什么才是真的好主意? That kind of amounts to hard coding your functionality once and for all, doesn't it? 这类似于一劳永逸地对您的功能进行硬编码,不是吗?

EDIT 编辑

I see in a comment that you say you want to 'chain function calls together'. 我在评论中看到你说你想“将函数调用连在一起”。

How about writing them one after the other? 一个接一个地写它们怎么样? We aren't in Haskell after all, F# will fire them in the order you write them. 毕竟我们不在Haskell中,F#会按照你编写它们的顺序触发它们。

If you want to be a little bit more functional, you could use continuations - each function takes an extra parameter which is the next function to execute. 如果你想要更具功能性,你可以使用continuation - 每个函数都需要一个额外的参数,这是下一个要执行的函数。 Almost monadic (I believe), except that in your case they appear to be actions, so there are no values to string through from one function to the next. 几乎是monadic(我相信),除了在你的情况下它们似乎是动作,所以没有值从一个函数到下一个函数串通。

Not sure if that helps: I think you'll have to try rephrasing your question, judging by the diversity of answers here. 不确定这是否有帮助:我认为你必须尝试改写你的问题,从这里答案的多样性来判断。

It seems like you are trying to do something in a very complicated way. 看起来你正试图以一种非常复杂的方式做某事。 That is sometimes necessary, but usually it isn't. 这有时是必要的,但通常不是。

Since you are asking this question, I assume that you have more experience in imperative languages. 既然你问这个问题,我认为你有更多的命令式语言经验。 It seems that the real solution to your problem is something completely different than a list of functions. 似乎问题的真正解决方案与功能列表完全不同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM