我能否让这个Haxe switch语句更加动态?

时间:2014-08-24 17:42:21

标签: haxe

这不重要,但我在想。在我的程序中的某个地方,我有一个switch语句,它会以递增的值被多次调用,因此所有的情况都应该按顺序执行。像定制的简单音序器。

像这样:

private function sequence_Crush(step:Int):Void
{
    switch(step) {
    case 1: {
        action_loadCueFile();
        seq.next(); //This calls the same function with an increased step
    }
    case 2: {
        action_saveSettings();
        seq.next();
    }

    /// EDIT: Some steps run ASYNC and an event triggers the next step in the sequence
    /// like this:

    case 3: {           
        events.once(ENGINE_EVENTS.cut_all_complete, seq.next);
        cutTracks();
    }

我的问题是,有没有办法在案例中替换手动编写的数字(1,2,3,4)并以某种方式使用计数器,宏可能?我试过放一个动态计数器,但Haxe编译器抱怨。

我尝试了什么:

    var st:Int = 1;
    switch(step) {
    case (st++): { // 1
        action_loadCueFile();
        seq.next();
    }
    case (st++): { // 2
        action_saveSettings();
        seq.next();
    }
    //... etc
  

构建因错误而停止(haxe.exe)

     

案例表达式必须是常量值或模式,而不是任意表达式

我的目标是JS并使用Haxe 3.1.3。我在actionscript和javascript中尝试了它,它工作正常。我想这样做的原因是,如果我想要添加或删除一个步骤,我必须手动重新组织其他每个案例编号。

P.S。我知道有其他方法可以按顺序对动作进行排序,但我喜欢这个,因为我在一个函数中拥有所有内容,并且很容易一目了然地看到执行顺序

感谢阅读: - )

3 个答案:

答案 0 :(得分:2)

在Haxe 3中,switch从JS / Flash样式简单匹配改为完全依赖于if / elseif / else语句,完全依赖于模式匹配,其中包含更多编译 - 时间特征,其中一个限制是你不能匹配一个变量,只能对抗常量。

您可以使用if (step==st++) {} elseif (step==st++) {} else {}语句链来获得几乎相同的效果。如果你真的沉迷于switch语法,你可以使用宏来获得“经典”切换行为。我不久前碰巧写了一个这样的宏,看看这个GIST:

https://gist.githubusercontent.com/jasononeil/5429516/raw/ad1085082530760aa394765d5cd5ebd61a5dbecb/ClassicSwitch.hx

然后您可以这样编码:

class Action
{
        static function main()
        {
                for (currentStep in 0...5) {
                        var i = 0;
                        ClassicSwitch.from(switch (currentStep) {
                                case i++: trace( 'Do step $i' );
                                case i++: trace( 'Do step $i' );
                                case i++: trace( 'Do step $i' );
                                case i++: trace( 'Do step $i' );
                                case i++: trace( 'Do step $i' );
                        });
                }
        }
}

这给了我输出:

  

Action.hx:14:执行第1步   Action.hx:15:执行第2步   Action.hx:16:执行第3步   Action.hx:17:执行第4步   Action.hx:18:执行第5步

答案 1 :(得分:2)

杰森几分钟就打败了我......

Haxe中的案例表达式必须是常量值或模式。

但是您可以通过以下几种方式实现所需的行为:(a)自定义语法,如$next和宏; (b)宏观转换为if-else块(Jason的回答); (c)没有宏和(误)使用图案防护装置。

自定义语法

随后快速而肮脏地实施它;它只支持case $next:,并且没有语法检查。

当找到case $next:时,宏检查先前的案例模式是否是单个常量整数i,在这种情况下,将模式重写为i + 1的值。

宏实施:

// SequenceSwitch.hx

import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.ExprTools;

class SequenceSwitch {

    public
    macro static function build():Array<Field> {
        var fields = Context.getBuildFields();
        for (f in fields)
            switch (f.kind) {
                case FFun(func) if (func.expr != null):
                    func.expr = ExprTools.map(func.expr, transf);
                case _:
            }
        return fields;
    }

    static function transf(e:Expr):Expr {
        return switch (e.expr) {
            case ESwitch(expr, cases, def):
                var ncases = [];
                var prev:Array<Expr> = null;
                for (c in cases) {
                    var cur = switch (c.values) {
                        case [{ expr : EConst(CIdent("$next")), pos : pos }] if (prev != null):
                            switch (prev) {
                                case [{ expr : EConst(CInt(i)) }]:
                                    var next = { expr : EConst(CInt(Std.string(Std.parseInt(i) + 1))), pos : pos };
                                    { values : [next], guard : c.guard, expr : c.expr };
                                case _:
                                    c;
                            }
                        case _:
                            c;
                    };
                    ncases.push(cur);
                    prev = cur.values;
                }
                { expr : ESwitch(expr, ncases, def), pos : e.pos };
            case _:
                e;
        }
    }

}

用法示例:

// Text.hx

@:build(SequenceSwitch.build())
class Test {

    static function main() {
        sequenceCrush(1);
    }

    static function sequenceCrush(step:Int) {
        switch (step) {
            case 1:
                trace("do one");
                sequenceCrush(++step);
            case $next:
                trace("do two");
                sequenceCrush(++step);
            case $next:
                trace("do three");
                sequenceCrush(++step);
            case _:
                trace("terminate");
        }
    }

}

没有宏/有警卫

使用警卫可以实现类似的行为:

static function sequenceCrush_guards(step:Int) {
    var st = 1;
    switch (step) {
        case next if (next == st++):
            trace("do one");
            sequenceCrush_guards(++step);
        case next if (next == st++):
            trace("do two");
            sequenceCrush_guards(++step);
        case next if (next == st++):
            trace("do three");
            sequenceCrush_guards(++step);
        case _:
            trace("terminate");
    }
}

答案 2 :(得分:0)

如果您的所有(或大多数)操作都是简单的函数调用,您也可以使用函数数组:

var actions = [sequence_Crush.bind(1), // if you want to avoid action index = step - 1
               action_loadCueFile,
               action_saveSettings,
               ...];

private function sequence_Crush(step:Int):Void
{
    while (step < actions.length)
    {
        actions[step++]();
    }
}

你也可以保持这种递归(actions[step++](); if (step < actions.length) { sequence_Crush(step))。