这不重要,但我在想。在我的程序中的某个地方,我有一个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。我知道有其他方法可以按顺序对动作进行排序,但我喜欢这个,因为我在一个函数中拥有所有内容,并且很容易一目了然地看到执行顺序
感谢阅读: - )
答案 0 :(得分:2)
在Haxe 3中,switch
从JS / Flash样式简单匹配改为完全依赖于if / elseif / else语句,完全依赖于模式匹配,其中包含更多编译 - 时间特征,其中一个限制是你不能匹配一个变量,只能对抗常量。
您可以使用if (step==st++) {} elseif (step==st++) {} else {}
语句链来获得几乎相同的效果。如果你真的沉迷于switch
语法,你可以使用宏来获得“经典”切换行为。我不久前碰巧写了一个这样的宏,看看这个GIST:
然后您可以这样编码:
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)
)。