简体   繁体   English

F#:需要帮助将C#转换为F#

[英]F#: Need help converting C# to F#

I'm trying to write a small little scripting engine for a bullet hell game and I would like to do it in F#. 我正在尝试为子弹地狱游戏编写一个小小的脚本引擎,我想在F#中进行。 I wrote some C# code to conceptualize it, but I'm having trouble porting it to F#. 我写了一些C#代码来概念化它,但是我把它移植到F#时遇到了麻烦。 The C# code is posted below, and I would like some help porting it to F#. C#代码发布在下面,我想帮助将它移植到F#。 I have a feeling the matching F# code will be significantly smaller. 我觉得匹配的F#代码会明显变小。 I'm open to any sort of creative solutions :) 我愿意接受任何创造性的解决方案:)

interface IRunner
{
    Result Run(int data);
}

struct Result
{
    public Result(int data, IRunner next)
    {
        Data = data;
        Next = next;
    }
    public int Data;
    public IRunner Next;
}

class AddOne : IRunner
{
    public Result Run(int data)
    {
        return new Result(data + 1, null);
    }
}

class Idle : IRunner
{
    public Result Run(int data)
    {
        return new Result(data, null);
    }
}

class Pair : IRunner
{
    IRunner _one;
    IRunner _two;

    public Pair(IRunner one, IRunner two)
    {
        _one = one;
        _two = two;
    }

    public Result Run(int data)
    {
        var res = _one.Run(data);
        if (res.Next != null)
            return new Result(res.Data, new Pair(res.Next, _two));
        return new Result(res.Data, _two);
    }
}

class Repeat : IRunner
{
    int _counter;
    IRunner _toRun;

    public Repeat(IRunner toRun, int counter)
    {
        _toRun = toRun;
        _counter = counter;
    }

    public Result Run(int data)
    {
        var res = _toRun.Run(data);
        if (_counter > 1)
        {
            if (res.Next != null)
                return new Result(res.Data,
                            new Pair(res.Next,
                                new Repeat(_toRun, _counter - 1)));
            return new Result(res.Data, new Repeat(_toRun, _counter - 1));
        }
        return res;
    }
}

class Sequence : IRunner
{
    IEnumerator<IRunner> _runner;

    public Sequence(IEnumerator<IRunner> runner)
    {
        _runner = runner;
    }
    public Result Run(int data)
    {
        var res = _runner.Current.Run(data);
        bool next = _runner.MoveNext();
        if (res.Next != null)
        {
            return new Result(res.Data,
                        new Pair(res.Next, new Sequence(_runner)));
        }

        return new Result(res.Data, new Sequence(_runner));
    }
}

Here's something that's almost a direct translation of the same solution strategy. 这几乎是对同一解决方案策略的直接翻译。

That said, I think there may be a better/simpler representation choice, I'm still mulling it over. 也就是说,我认为可能有一个更好/更简单的表示选择,我仍然在考虑它。

type Runner = int -> Result
and Result = Result of int * option<Runner>

let AddOne = fun x -> Result(x+1, None)

let Idle = fun x -> Result(x, None)

let rec Pair(r1,r2) = fun x ->
    match r1 x with
    | Result(data,None) -> Result(data, Some(r2))
    | Result(data,Some(next)) -> Result(data,Some(Pair(next,r2)))

let rec Repeat r n = fun x ->
    if n = 0 then r x else
    match r x with
    | Result(data,None) -> Result(data, Some(Repeat r (n-1)))
    | Result(data,Some(next)) -> Result(data, Some(Pair(next, Repeat r (n-1))))

EDIT 编辑

Here's another way that's a little more refined... am still trying to see if there's a good way to work in a "list", since the results seem isomorphic to cons cells... 这是另一种更精致的方式......我仍然试图看看是否有一种很好的方式在“列表”中工作,因为结果似乎与cons细胞同构......

type Runner = Runner of (int -> int * option<Runner>)

let AddOne = Runner(fun x -> x+1, None)

let Idle = Runner(fun x -> x, None)

let rec Pair(Runner(r1),R2) = Runner(fun x ->
    match r1 x with
    | data,None -> data, Some(R2)
    | data,Some(next) -> data, Some(Pair(next,R2)))

let rec Repeat (Runner(r) as R) n = Runner(fun x ->
    if n = 0 then r x else
    match r x with
    | data,None -> data, Some(Repeat R (n-1))
    | data,Some(next) -> data, Some(Pair(next, Repeat R (n-1))))

EDIT 编辑

One more version, it uses lists, but now I've a feeling for what's weird here... 还有一个版本,它使用了列表,但现在我对这里有什么奇怪的感觉......

type Runner = Runner of (int -> int * list<Runner>)

let AddOne = Runner(fun x -> x+1, [])

let Idle = Runner(fun x -> x, [])

let rec Pair(Runner(r1),R2) = Runner(fun x ->
    match r1 x with
    | data,xs -> data, xs @ [R2]) // inefficient

let rec Repeat (Runner(r) as R) n = Runner(fun x ->
    if n = 0 then r x else
    match r x with
    | data,xs -> data, xs @ List.init (n-1) (fun _ -> R)) // inefficient

It's almost just like an 'Action queue', a list of int->int functions. 它几乎就像一个'Action queue',一个int->int函数列表。 But each guy can produce some 'suffix actions' that run immediately after him (but before the remaining work in the would-be queue), and trying to maintain the ordering with a purely functional data structure is potentially inefficient (without the right tree/queue library at hand). 但是每个人都可以产生一些“后缀动作”,这些动作在他之后立即运行(但是在剩余的工作在未来的队列之前),并且尝试用纯粹的功能数据结构维护排序可能是低效的(没有正确的树/手头的队列库)。 It would be interesting to know how this will be used/consumed, as perhaps a small change there might allow for a completely different strategy. 知道如何使用/消费这将是有趣的,因为可能会有一个小的变化可能允许完全不同的策略。

Forget the C#, go back to the design documents (or whatever) and re-implement. 忘记C#,回到设计文档(或其他)并重新实现。 I mean, literally, forget the C#. 我的意思是,从字面上看,忘了C#。 The worst thing you can do in F# is to write C#. 你在F#中可以做的最糟糕的事情就是编写C#。 This is, of course, an instance of a generic rule: The worst thing you can do in language X is to write a program in language Y . 当然,这是一个通用规则的实例: 你在语言X中可以做的最糟糕的事情是用Y语言编写一个程序 Bind X and Y as you wish. 根据需要绑定X和Y.

I am assuming that IRunner and Result are predefined, since if not you should redesign the system to be more focused on FP concepts without all this inheritance. 我假设IRunner和Result是预定义的,因为如果不是,你应该重新设计系统,以更加专注于FP概念,而不需要所有这些继承。

Anyway, here is a binary (I believe) counterpoint to the given example 无论如何,这是给定示例的二进制(我相信)对应点

type AddOne =
    interface IRunner with
        member this.Run(data) = new Result(data+1, null)

type Idle =
    interface IRunner with
        member this.Run(data) = new Result(data, null)

type Pair(one:IRunner, two:IRunner) =
    interface IRunner with
        member this.Run(data) =
            let res = one.Run(data)
            if res.Next <> null then
                new Result(res.Data, new Pair(res.Next, two))
            else
                new Result(res.Data, two)

type Repeat(toRun:IRunner, counter:int) =
    interface IRunner with
        member this.Run(data) =
            let res = toRun.Run(data)
            if counter > 1 then
                if res.Next <> null then
                    new Result(res.Data, new Pair(res.Next, new Repeat(toRun, counter - 1)))
                else
                    new Result(res.Data, new Repeat(toRun, counter - 1))
            else
                res

type Sequence(runner:System.Collections.Generic.IEnumerator<IRunner>) =
    interface IRunner with
        member this.Run(data) =
            let res = runner.Current.Run(data)
            let next = runner.MoveNext()
            if res.Next <> null then
                new Result(res.Data, new Pair(res.Next, new Sequence(runner)))
            else
                new Result(res.Data, new Sequence(runner))

You may consider using the built-in sequence support in F# (along with the very nice sequence expressions ). 您可以考虑使用F#中的内置序列支持(以及非常好的序列表达式 )。 This example is a little contrived but you could think of the program as a sequence of sequences, the "action queue" that Brian was hinting at. 这个例子有点人为,但你可以把程序想象成一系列序列,Brian正在暗示的“行动队列”。 By adding the outer sequence it allows the inner sequence to fully control it's lifetime. 通过添加外部序列,它允许内部序列完全控制它的寿命。

// an infinite sequence, repeat the last element of the underlying sequence
// now we can avoid using option types or null/boolean checks in our fold
let lastInfinite (a:seq<'a>) = seq {
    let last = ref Unchecked.defaultof<'a>
    for i in a do
        last := i
        yield i
    let last' = !last
    while true do
        yield last'
}

let addOne = seq [ fun x -> x + 1 ]
let idle = seq [ id<int> ]
let repeat run counter = Seq.truncate counter (lastInfinite run)
let sequence = Seq.concat
let pair one two = lastInfinite (Seq.append one two)

let program = seq [ repeat idle 5;
                    repeat addOne 100;
                    idle; ]

// use Seq.scan if you need lazy access to the intermediate results
// or perhaps Seq.map...
let result = Seq.concat program
             |> Seq.fold (fun state value -> value state) 0

printfn "result: %A" result

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

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