如何优化多重(矩阵)开关/案例算法?

发布于 2024-08-09 17:22:16 字数 1521 浏览 3 评论 0原文

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

//        | 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;
      }
    }

代码相当简单,但您必须想象更多“switch / case”会变得更复杂。

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

(已用法语提出问题 此处)。

编辑:(来自 Dran Dane 对下面评论的回复。这些也可以放在这个更显眼的地方!)
优化”可以理解为编写更少的代码、更少的“switch / case”。这个想法是为了提高可读性、可维护性,而不是性能。

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

Is it possible to optimize this kind of (matrix) algorithm:

//        | 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;
      }
    }

The code is fairly simple but you have to imagine more complex with more "switch / case".

I work with 3 variables. According they take the values 1, 2, 3 or a, b, c or alpha, beta, charlie have different processes to achieve. Is it possible to optimize it any other way than through a series of "switch / case?

(Question already asked in french here).

Edit: (from Dran Dane's responses to comments below. These might as well be in this more prominent place!)
"optimize" is to be understood in terms of having to write less code, fewer "switch / case". The idea is to improve readability, maintainability, not performance.

There is maybe a way to write less code via a "Chain of Responsibility" but this solution is not optimal on all points, because it requires the creation of many objects in memory.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

咽泪装欢 2024-08-16 17:22:17

我曾经遇到过完全相同的问题,尽管 5 参数嵌套开关的内在混乱。我想,为什么要自己输入所有这些 O(N5) 情况,如果编译器可以为我做到这一点,为什么还要发明“嵌套”函数名称。所有这一切导致了“嵌套专用模板开关”引用“专用模板数据库”。

写起来有点复杂。但我发现这是值得的:它产生了一个“知识”数据库,非常易于维护、调试、添加等......而且我必须承认:一种自豪感。

// 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 }; };

知识库的“前向声明”:嵌套开关的“api”。

 // 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;
}

I had exactly the same problem once, albeit for an immanent mess of a 5-parameter nested switch. I figured, why type all these O(N5) cases myself, why even invent 'nested' function names if the compiler can do this for me. And all this resulted in a 'nested specialized template switch' referring to a 'specialized template database'.

It's a little complicated to write. But I found it worth it: it results in a 'knowledge' database that is very easy to maintain, to debug, to add to etc... And I must admit: a sense of pride.

// the return type: might be an object actually _doing_ something
struct Result {
  const char* value;
  Result(): value(NULL){}
  Result( const char* p ):value(p){};
};

Some variable types for switching:

 // types used:
 struct A { enum e { a1, a2, a3 }; };
 struct B { enum e { b1, b2 }; };
 struct C { enum e { c1, c2 }; };

A 'forward declaration' of the knowledge base: the 'api' of the nested switch.

 // 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"; };

The actual switching logic (condensed)

 // 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();
         }
       };
     };

   };
 };

And the knowledge base itself

 // 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"; }

This is how it can be used.

int main()
{
     // usage:
     Result r = Switch::value( A::a1, B::b2, C::c2 ); 
     return 0;
}
若水微香 2024-08-16 17:22:17

是的,肯定有更简单的方法可以做到这一点,既更快更简单。这个想法与Alex Martelli 提出的基本相同。不要将问题视为二维问题,而是将其视为某个一维查找表。

这意味着组合 var、subvar、subsubvar 等来获得一个唯一的键并将其用作查找表入口点。

执行此操作的方法取决于所使用的语言。使用 python 组合 var、subvar 等来构建一个元组并将其用作字典中的键就足够了。

使用 C 或此类语言,通常更简单地将每个键转换为枚举,然后使用逻辑运算符将它们组合起来,以获得可以在 switch 中使用的一个数字(这也是使用 switch 而不是使用级联 if 进行字符串比较的一种简单方法)。这样做你还可以获得另一个好处。初始切换的不同分支中的几种处理是相同的,这是很常见的。对于最初的形式来说,很难让这一点变得显而易见。您可能会调用一些相同的函数,但它们位于代码中的不同点。现在,您可以在编写开关时将相同的情况分组。

我在生产代码中多次使用了这种转换,并且它很容易执行和维护。

总之你可以得到这样的东西......混合功能显然取决于你的应用程序的具体情况。

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;
}

Yes, there is definitely easier way to do that, both faster and simpler. The idea is basically the same as proposed by Alex Martelli. Instead of seeing you problem as bi-dimentional, see it as some one dimension lookup table.

It means combining var, subvar, subsubvar, etc to get one unique key and use it as your lookup table entry point.

The way to do it depends on the used language. With python combining var, subvar etc. to build a tuple and use it as key in a dictionnary is enough.

With C or such it's usually simpler to convert each keys to enums, then combine them using logical operators to get just one number that you can use in your switch (that's also an easy way to use switch instead of string comparizons with cascading ifs). You also get another benefit doing it. It's quite usual that several treatments in different branches of the initial switch are the same. With the initial form it's quite difficult to make that obvious. You'll probably have some calls to the same functions but it's at differents points in code. Now you can just group the identical cases when writing the switch.

I used such transformation several times in production code and it's easy to do and to maintain.

Summarily you can get something like this... the mix function obviously depends on your application specifics.

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;
}
十雾 2024-08-16 17:22:17

也许您想要的是代码生成?

#! /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))

当然,这很难阅读,您可能真的需要一个更好的模板语言来完成您的肮脏工作,但这将减轻您任务的某些部分。

Perhaps what you want is code generation?

#! /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))

This is pretty hard to read, of course, and you might really want a nicer template language to do your dirty work, but this will ease some parts of your task.

只怪假的太真实 2024-08-16 17:22:17

如果 C++ 是一个选项,我会尝试使用虚函数,也许还可以使用双重调度。这可以让它变得更加干净。但只有当你有更多的案例时,它才可能得到回报。

这篇DDJ.com 上的文章可能是一个不错的条目。

If C++ is an option i would try using virtual function and maybe double dispatch. That could make it much cleaner. But it will only probably pay off only if you have many more cases.

This article on DDJ.com might be a good entry.

念三年u 2024-08-16 17:22:17

如果您只是想消除两级 switch/case 语句(并节省一些垂直空间),您可以将两个变量值编码为单个值,然后打开它:

// 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;
}

If you're just trying to eliminate the two-level switch/case statements (and save some vertical space), you can encode the two variable values into a single value, then switch on it:

// 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;
}
一绘本一梦想 2024-08-16 17:22:17

如果您的语言是 C#,并且您的选择足够短并且不包含特殊字符,您可以使用反射并只需几行代码即可完成。这样,就可以使用框架提供的函数指针数组,而不是手动创建和维护函数指针数组!

像这样:

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
}

If your language is C#, and your choices are short enough and contain no special characters you can use reflection and do it with just a few lines of code. This way, instead of manually creating and maintaining an array of function pointers, use one that the framework provides!

Like this:

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
}
冷情 2024-08-16 17:22:17

来自 developpez.com
是的,你可以优化它,让它变得更加干净。你不能使用这样的“链
工厂的责任”:

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;
    }
}

然后,就像您的流程使用 canXXX 实现接口流程一样,您可以轻松使用:

new ProcessFactory().getProcess(var,subvar).launch();

Solution from developpez.com
Yes, you can optimize it and make it so much cleaner. You can not use such a "Chain of
Responsibility" with a Factory:

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;
    }
}

Then just as your processes implement an interface process with canXXX you can easily use:

new ProcessFactory().getProcess(var,subvar).launch();
西瑶 2024-08-16 17:22:16

听起来您想要的是一个'有限状态机',您可以使用这些案例激活不同的进程或“状态”。在 C 中,这通常是通过函数指针数组(矩阵)来完成的。

因此,您本质上创建一个数组并将正确的函数指针放在正确的索引处,然后使用“var”作为正确“进程”的索引,然后调用它。您可以在大多数语言中执行此操作。这样,机器的不同输入会激活不同的过程并使其进入不同的状态。这对于许多应用程序来说非常有用;我自己在MCU开发中一直使用它。

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

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

It sounds like what you want is a 'Finite State Machine' where using those cases you can activate different processes or 'states'. In C this is usually done with an array (matrix) of function pointers.

So you essentially make an array and put the right function pointers at the right indicies and then you use your 'var' as an index to the right 'process' and then you call it. You can do this in most languages. That way different inputs to the machine activate different processes and bring it to different states. This is very useful for numerous applications; I myself use it all of the time in MCU development.

Edit: Valya pointed out that I probably should show a basic model:

stateMachine[var1][var2]();   // calls the right 'process' for input var1, var2
惯饮孤独 2024-08-16 17:22:16

这个问题没有好的答案:-(

因为很多响应取决于

  • 有效目标(“优化”是什么意思,嵌套开关的令人不快的地方)
  • 将应用此构造的上下文(应用程序隐含的最终需求是什么)

TokenMacGuy 明智地询问了目标,我花时间在法国网站上检查了问题及其答复,但我仍然对目标感到困惑...... Dran Dane。最新的回应似乎指向减少代码量/提高可读性,但让我们回顾一下:

  • 处理速度:不是问题,嵌套开关非常有效,可能需要不到 3 次乘法才能获得映射表的索引,但是 可读性:是的,这可能
  • 是一个问题,随着变量和级别数量的增加,组合爆炸开始出现,而且 switch 语句的格式往往会将分支点和相关值分散在很长的垂直方向上。在本例中,使用 fct 初始化的 3 维(或更多)表。指针将分支值和要调用的函数重新放在一行上。
  • 编写更少的代码:抱歉,这里没有太多帮助;归根结底,我们需要考虑相对较多的组合,并且“地图”,无论其形式如何,都必须写在某个地方。诸如 TokenMacGuy 之类的代码生成器可能会派上用场,但在这种情况下它似乎有点矫枉过正。生成器有其用武之地,但我不确定这里的情况是否如此。两种情况之一:如果变量和级别的数量足够小,则生成器不值得(设置它比首先编写实际代码花费更多时间),如果变量和级别的数量是重要的是,生成的代码难以阅读,难以维护...)

简而言之,我的建议关于使代码更具可读性(并且速度更快)写)是法国网站上描述的表格/矩阵方法。

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

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

如果组合空间相对稀疏,则可能提示使用上述解决方案的情况是 (开关“树”中的许多“分支”未使用)或如果某些功能需要不同的参数集;对于这两种情况,我想插入Joel Goodwin 首先在这里提出的解决方案,该解决方案本质上将多个级别的各个键组合成一个更长的键(如果需要,可以使用分隔符) ,本质上将问题扁平化回一个很长的单级切换语句

现在......

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

很抱歉用相当抽象的术语来谈论这个问题,这只是缺乏应用程序细节......重点仍然是:挑战我们需要这个大调度树的想法;考虑整个应用程序的替代方法。

阿洛斯,好机会! ;-)

There are no good answers to this question :-(

because so much of the response depends on

  • The effective goals (what is meant by "optimize", what is unpleasing about the nested switches)
  • The context in which this construct is going to be applied (what are the ultimate needs implicit to the application)

TokenMacGuy was wise to ask about the goals. I took the time to check the question and its replies on the French site and I'm still puzzled as to the goals... Dran Dane latest response seems to point towards lessening the amount of code / improving readability but let's review for sure:

  • Processing Speed: not an issue the nested switches are quite efficient, possibly a tat less than 3 multiplications to get an index into a map table, but maybe not even.
  • Readability: yes possibly an issue, As the number of variables and level increases the combinatorial explosion kicks in, and also the format of the switch statement tends to spread the branching spot and associated values over a long vertical stretch. In this case a 3 dimension (or more) table initialized with fct. pointers puts back together the branching values and the function to be call on on a single line.
  • Writing less code: Sorry not much help here; at the end of the day we need to account for a relatively high number of combinations and the "map", whatever its form, must be written somewhere. Code generators such as TokenMacGuy's may come handy, it does seem a bit of an overkill in this case. Generators have their place, but I'm not sure it is the case here. One of two case: if the number of variables and level is small enough, the generator is not worth it (takes more time to set it up than to write the actual code in the first place), if the number of variables and levels is significant, the generated code is hard to read, hard to maintain...)

In a nutshell, my recommendation with regards to making the code more readable (and a bit faster to write) is the table/matrix approach described on the French site.

This solution is in two part:
a one time initialization of a 3 dimensional array (for 3 levels); (or a "fancier" container structure if preferred: a tree for example) . This is done with code like:

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

And a relatively simple snippet wherever the functions need to be called:

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

A case which may prompt one NOT using the solution above are if the combination space is relatively sparsely populated (many "branches" in the switch "tree" are not used) or if some of the functions require a different set of parameters; For both of these cases, I'd like to plug a solution Joel Goodwin proposed first here, and which essentially combines the various keys for the several level into one longer key (with separator character if need be), essentially flattening the problem back to a long, but single level switch statement.

Now...

The real discussion should be about why we need such a mapping/decision-tree in the first place. To answer this unfortunately requires understanding the true nature of the underlying application. To be sure I'm not saying that this is indicative of bad design. A big dispatching section may make sense in some applications. However, even with the C language (which the French Site contributors seemed to disqualify to Object Oriented design), it is possible to adopt Object oriented methodology and patterns. Anyway I'm diverging...) It is possible that the application would overall be better served with alternative design patterns where the "information tree about what to call when" has been distributed in several modules and/or several objects.

Apologies to speak about this in rather abstract terms, it's just the lack of application specifics... The point remains: challenge the idea that we need this big dispatching tree; think of alternative approaches to the application at large.

Alors, bonne chance! ;-)

追风人 2024-08-16 17:22:16

根据语言的不同,某种形式的哈希映射以 (var, subvar) 对作为键,以一流函数作为值(或者您的语言提供的任何最接近的值,例如在 Java 中扩展一些适当的接口的类)可能会提供最高的性能——并且根据键从映射中获取适当的函数(或其他;-)并执行它的完全简洁性,为熟悉的读者带来了高可读性与语言和此类功能性习语一起。

Depending on the language, some form of hash map with the pair (var, subvar) as the key and first-class functions as the values (or whatever your language offers to best approximate that, e.g. instances of classes extending some proper interface in Java) is likely to provide top performance -- and the utter conciseness of fetching the appropriate function (or whatever;-) from the map based on the key, and executing it, leads to high readability for readers familiar with the language and such functional idioms.

地狱即天堂 2024-08-16 17:22:16

函数指针的想法可能是最好的(根据 mjv,Shhnap)。但是,如果每种情况下的代码都相当小,则可能会过度,并导致比预期更多的混淆。在这种情况下,我可能会实现一些快速且易于阅读的内容,如下所示:

string decision = var1.ToString() + var2.ToString() + var3.ToString();
switch(decision)
{
    case "1aa":
        ....
    case "1ab":
        ....

}

不熟悉您的特定场景,因此也许之前的建议更合适。

The idea of a function pointer is probably best (as per mjv, Shhnap). But, if the code under each case is fairly small, it may be overkill and result in more obfuscation than intended. In that case, I might implement something snappy and fast-to-read like this:

string decision = var1.ToString() + var2.ToString() + var3.ToString();
switch(decision)
{
    case "1aa":
        ....
    case "1ab":
        ....

}

Unfamiliar with your particular scenario so perhaps the previous suggestions are more appropriate.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文