Algorithm 如何优化多重(矩阵)切换/案例算法?

Algorithm 如何优化多重(矩阵)切换/案例算法?,algorithm,matrix,switch-statement,Algorithm,Matrix,Switch Statement,是否可以优化这种(矩阵)算法: // | case 1 | case 2 | case 3 | // ------|--------|--------|--------| // | | | | // case a| a1 | a2 | a3 | // | | | | // case b| b1 | b2 | b3 |

是否可以优化这种(矩阵)算法:

//        | case 1 | case 2 | case 3 |
//  ------|--------|--------|--------|
//        |        |        |        |
//  case a|   a1   |   a2   |   a3   |
//        |        |        |        |
//  case b|   b1   |   b2   |   b3   |
//        |        |        |        |
//  case c|   c1   |   c2   |   c3   |
//        |        |        |        |

switch (var)
    {
      case 1:
      switch (subvar)
      {
        case a:
          process a1;
        case b:
          process b1;    
        case c:
          process c1;
      }
      case 2:
      switch (subvar)
      {
        case a:
          process a2;
        case b:
          process b2;    
        case c:
          process c2;
      }
      case 3:
      switch (subvar)
      {
        case a:
          process a3;
        case b:
          process b3;    
        case c:
          process c3;
      }
    }
代码相当简单,但您必须想象更复杂的“开关/案例”

我处理3个变量。根据他们取的值1、2、3或a、b、c或alpha、beta,查理有不同的过程来实现。除了通过一系列“开关/外壳”之外,是否有可能以其他方式对其进行优化

(问题已用法语提出)

编辑:(来自Dran Dane对下面评论的回复。这些评论也可以放在这个更显眼的地方!)
“优化”是指编写更少的代码,更少的“开关/案例”。其目的是提高可读性、可维护性,而不是性能


也许有一种方法可以通过“责任链”编写更少的代码,但这种解决方案并非在所有方面都是最优的,因为它需要在内存中创建许多对象。

听起来你想要的是一个可以使用这些情况激活不同进程或“状态”的地方。在C中,这通常是通过数组(矩阵)完成的函数指针的定义

所以你基本上是做一个数组,把正确的函数指针放在正确的标记上,然后用你的“var”作为正确的“进程”的索引,然后调用它。在大多数语言中都可以这样做。这样,机器的不同输入会激活不同的进程,并使其处于不同的状态。这对很多人来说非常有用我自己在MCU开发中一直使用它

编辑:Valya指出我可能应该展示一个基本模型:

stateMachine[var1][var2]();   // calls the right 'process' for input var1, var2

也许你想要的是代码生成

#! /usr/bin/python
first = [1, 2, 3]
second = ['a', 'b', 'c']

def emit(first, second):
    result = "switch (var)\n{\n"
    for f in first:
        result += " case {0}:\n switch (subvar)\n {{\n".format(f)
        for s in second:
            result += "  case {1}:\n   process {1}{0};\n".format(f,s)
        result += " }\n"
    result += "}\n"
    return result

print emit(first,second)
#file("autogen.c","w").write(emit(first,second))
当然,这很难理解,您可能真的需要一种更好的模板语言来完成您的脏活,但这将减轻您的某些任务。

这个问题没有好的答案:-( 因为如此多的反应取决于

  • 有效的目标(什么是“优化”,嵌套交换机的不愉快之处是什么)
  • 将应用此构造的上下文(应用程序隐含的最终需求是什么)
TokenMacGuy很明智地询问了目标。我花时间在法国网站上查看了问题及其回复,但我仍然对目标感到困惑……Dran Dane的最新回应似乎指向减少代码量/提高可读性,但让我们回顾一下:

  • 处理速度:这不是一个问题,嵌套的开关非常有效,可能需要少于3次的乘法才能将索引放入映射表,但可能甚至不够
  • 可读性:是的,这可能是一个问题,因为变量和级别的数量增加了组合爆炸,而且switch语句的格式倾向于将分支点和相关值分散在一个较长的垂直延伸上。在这种情况下,是三维(或更高)用fct.pointers初始化的表将分支值和要在一行上调用的函数放回一起
  • 编写更少的代码:抱歉,这里没有太多帮助;在一天结束时,我们需要考虑相对较多的组合和“映射”“,无论其形式如何,都必须在某个地方编写。像TokenMacGuy这样的代码生成器可能会派上用场,在这种情况下似乎有点过火。生成器有自己的位置,但我不确定这里的情况是否如此。两种情况之一:如果变量和级别的数量足够小,那么生成器就不值得(设置它比首先编写实际代码需要更多的时间),如果变量和级别的数量很大,则生成的代码很难读取,很难维护…)
简而言之,关于使代码更具可读性(并且编写速度更快),我的建议是在法国网站上介绍的表格/矩阵方法。

此解决方案分为两部分:
一次初始化一个三维数组(对于3个级别);(或者一个“fancier”容器结构,如果愿意的话:例如一棵树)。这是通过以下代码完成的:

// This is positively more compact / readable
...
FctMap[1][4][0] = fctAlphaOne;
FctMap[1][4][1] = fctAlphaOne;
..
FctMap[3][0][0] = fctBravoCharlie4;
FctMap[3][0][1] = NULL;   // impossible case
FctMap[3][0][2] = fctBravoCharlie4;    // note how the same fct may serve in mult. places
以及一个相对简单的代码段,用于调用函数:

if (FctMap[cond1][cond2][cond3]) {
  retVal = FctMap[cond1][cond2][cond3](Arg1, Arg2);
  if (retVal < 0)
      DoSomething(); // anyway we're leveraging the common api to these fct not the switch alternative ....
}
if(FctMap[cond1][cond2][cond3]){
retVal=FctMap[cond1][cond2][cond3](Arg1,Arg2);
如果(返回值<0)
DoSomething();//无论如何,我们利用的是这些fct的通用api,而不是交换机替代方案。。。。
}
如果组合空间相对稀疏,则可能提示不使用上述解决方案的情况为(未使用交换机“树”中的许多“分支”)或者如果某些函数需要一组不同的参数;对于这两种情况,我想插入Joel Goodwin首先提出的解决方案,该解决方案基本上将多个级别的各种键组合成一个较长的键(如果需要,使用分隔符),本质上是将问题拉回一个较长的单级切换语句

现在。。。 真正的讨论应该是为什么我们首先需要这样一个映射/决策树。不幸的是,要回答这个问题,需要了解底层应用程序的真实性质。我并不是说这表明设计不好。在某些应用程序中,一个大的调度部分可能是有意义的wever,即使使用C语言(法国网站投稿人似乎不具备面向对象设计的资格),也有可能采用面向对象的方法和模式。无论如何,我有分歧…)有可能应用程序总体上会更好地使用“关于何时调用的信息树”已分布在多个模块和/或多个对象中

很抱歉用相当抽象的方式谈论这件事
string decision = var1.ToString() + var2.ToString() + var3.ToString();
switch(decision)
{
    case "1aa":
        ....
    case "1ab":
        ....

}
switch (mix(var, subvar))
{
      case a1:
          process a1;
      case b1:
          process b1;    
      case c1:
          process c1;
      case a2:
          process a2;
      case b2:
          process b2;    
      case c2:
          process c2;
      case a3:
          process a3;
      case b3:
          process b3;    
      case c3:
          process c3;
}
// the return type: might be an object actually _doing_ something
struct Result {
  const char* value;
  Result(): value(NULL){}
  Result( const char* p ):value(p){};
};
 // types used:
 struct A { enum e { a1, a2, a3 }; };
 struct B { enum e { b1, b2 }; };
 struct C { enum e { c1, c2 }; };
 // template database declaration (and default value - omit if not needed)
 // specializations may execute code in stead of returning values...
 template< A::e, B::e, C::e > Result valuedb() { return "not defined"; };
 // template layer 1: work away the first parameter, then the next, ...
 struct Switch {

   static Result value( A::e a, B::e b, C::e c ) {
     switch( a ) {
          case A::a1: return SwitchA<A::a1>::value( b, c );
          case A::a2: return SwitchA<A::a2>::value( b, c );
          case A::a3: return SwitchA<A::a3>::value( b, c );
          default: return Result();
     }
   }

   template< A::e a > struct SwitchA {

     static Result value( B::e b, C::e c ) {
       switch( b ) {
            case B::b1: return SwitchB<a, B::b1>::value( c );
            case B::b2: return SwitchB<a, B::b2>::value( c );
            default: return Result();
       }
     }

     template< A::e a, B::e b > struct SwitchB {

       static Result value( C::e c ) {
         switch( c ) {
               case C::c1: return valuedb< a, b, C::c1 >();
               case C::c2: return valuedb< a, b, C::c2 >();
               default: return Result();
         }
       };
     };

   };
 };
 // the template database
 //
 template<> Result valuedb<A::a1, B::b1, C::c1 >() { return "a1b1c1"; }
 template<> Result valuedb<A::a1, B::b2, C::c2 >() { return "a1b2c2"; }
int main()
{
     // usage:
     Result r = Switch::value( A::a1, B::b2, C::c2 ); 
     return 0;
}
// Assumes var is in [1,3] and subvar in [1,3]
// and that var and subvar can be cast to int values
switch (10*var + subvar)
{
    case 10+1:
        process a1;
    case 10+2:
        process b1;
    case 10+3:
        process c1;  
//
    case 20+1:
        process a2;
    case 20+2:
        process b2;
    case 20+3:
        process c2;  
//
    case 30+1:
        process a3;
    case 30+2:
        process b3;
    case 30+3:
        process c3;
//
    default:
        process error;
}
using System.Reflection;
...

void DispatchCall(string var, string subvar)
{
  string functionName="Func_"+var+"_"+subvar;
  MethodInfo m=this.GetType().GetMethod(fName);
  if (m == null) throw new ArgumentException("Invalid function name "+ functionName);
  m.Invoke(this, new object[] { /* put parameters here if needed */ });
}

void Func_1_a()
{
   //executed when var=1 and subvar=a
}

void Func_2_charlie()
{
   //executed when var=2 and subvar=charlie
}
public class ProcessFactory {
    private ArrayList<Process> processses = null;

    public ProcessFactory(){
        super();

        processses = new ArrayList<Process>();

        processses.add(new ProcessC1());
        processses.add(new ProcessC2());
        processses.add(new ProcessC3());
        processses.add(new ProcessC4());                    
        processses.add(new ProcessC5(6));                   
        processses.add(new ProcessC5(22));
    }

    public Process getProcess(int var, int subvar){
        for(Process process : processses){
            if(process.canDo(var, subvar)){
                return process;
            }
        }

        return null;
    }
}
new ProcessFactory().getProcess(var,subvar).launch();