开发者

F#: Storing and mapping a list of functions

开发者 https://www.devze.com 2022-12-11 23:57 出处:网络
I have a number of events that happen in a game. I want to control the time and order at which these events occur.

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

Event 2: Clear the text on the screen

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.

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). The function that shows text would return a copy of itself for N-1 frames then it would return Event2 which clears the te开发者_如何学Pythonxt. 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#. My goal is to do an equivalent or better solution in 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):

> 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:

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. 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.

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.

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. 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.

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消