是否可以优化这种(矩阵)算法:
// |案例1 |案例2 |案例3 |// —— | ——– | ——– | ——– |// | | | |//案例a | a1 | …
因为这么多的回应取决于
TokenMacGuy明智地询问目标。我花时间检查问题及其在法国网站上的回复,我仍然对目标感到疑惑...... Dran Dane最近的回应似乎指向减少代码量/提高可读性但是让我们回顾一下:
简而言之,我的建议 的 关于使代码更具可读性 强> (并且写得快一点)是法国网站上描述的表格/矩阵方法。
该解决方案分为两部分: 一次初始化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首先提出的解决方案 强> 基本上,它基本上将几个级别的各种键组合成一个更长的键(如果需要,还有分隔符) 的 将问题归结为一个冗长但单级的switch语句 强> 。
真正的讨论应该是 的 为什么我们首先需要这样的映射/决策树 强> 。不幸的是,要回答这个问题需要了解底层应用程序的真实性质。可以肯定的是,我并不是说这表明设计不好。在某些应用程序中,一个大的调度部分可能有意义。但是,即使使用C语言(法语站点贡献者似乎不符合面向对象的设计),也可以采用面向对象的方法和模式。无论如何,我正在分歧......)应用程序可能总体上更好地服务于替代设计模式,其中“关于什么时候调用的信息树”已经分布在几个模块和/或几个对象中。
抱歉以相当抽象的方式谈论这个问题,只是缺乏应用程序细节......重点仍然是:挑战我们需要这个大型调度树的想法;想一想整个应用程序的替代方法。
Alors,bonne机会! ;-)
如果您只是想消除两级开关/ 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; }
函数指针的想法可能是最好的(根据mjv,Shhnap)。但是,如果每种情况下的代码都相当小,则可能过度杀戮并导致比预期更多的混淆。在这种情况下,我可能会实现像这样快速,快速阅读的东西:
string decision = var1.ToString() + var2.ToString() + var3.ToString(); switch(decision) { case "1aa": .... case "1ab": .... }
不熟悉您的特定情况,因此以前的建议可能更合适。
也许你想要的是代码生成?
#! /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))
当然,这很难阅读,你可能真的想要一个更好的模板语言来完成你的肮脏工作,但这将减轻你的任务的某些部分。
根据语言,使用某种形式的哈希映射 (var, subvar) 作为值的关键和第一类函数(或者您的语言提供的最佳近似值,例如,在Java中扩展一些适当接口的类的实例)可能提供最佳性能 - 并且获取适当函数的完全简洁(或者其他;-)基于键的地图,并执行它,为熟悉语言和这些功能习语的读者带来高可读性。
(var, subvar)
我有一次完全相同的问题,虽然是一个5参数嵌套开关的内在混乱。我想,为什么要输入所有这些O(N 五 如果编译器可以为我做这个,为什么甚至发明'嵌套'函数名称。所有这些导致了一个'嵌套的专用模板开关',指的是'专用模板数据库'。
写起来有点复杂。但我发现它值得:它导致了一个“知识”数据库 非常 易于维护,调试,添加等...我必须承认:自豪感。
// 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; }
解决方案 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();
如果您的语言是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 }
如果C ++是一个选项,我会尝试使用虚函数,也许双重调度。这可以使它更清洁。但只有当你有更多的案件时,它才会得到回报。
这个 关于DDJ.com的文章 可能是一个很好的入口。