Как я могу оптимизировать несколько (матричных) переключателей / алгоритм случая?

Авторы платформы сами считают ApplicationException бесполезным:

http://blogs.msdn.com/kcwalina/archive/2006/06/23/644822.aspx

с хорошим продолжением здесь:

http://blogs.msdn.com/kcwalina/archive/2006/07/05/657268.aspx

, Когда в сомнении, я следую их книжному Руководству по проектированию Платформы.

http://www.amazon.com/Framework-Design-Guidelines-Conventions-Development/dp/0321246756

тема сообщения в блоге далее обсуждена там.

армированный пластик

5
задан mjv 8 November 2009 в 04:46
поделиться

11 ответов

Решение от developpez.com Да, вы можете оптимизировать его и сделать его намного чище. Вы не можете использовать такую ​​«Цепочку ответственности» с 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;
    }
}

Тогда так же, как ваши процессы реализуют интерфейсный процесс с canXXX, вы можете легко использовать:

new ProcessFactory().getProcess(var,subvar).launch();
0
ответ дан 18 December 2019 в 06:23
поделиться

Возможно, вам нужна генерация кода?

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

Это, конечно, довольно сложно читать, и вам действительно может понадобиться более хороший язык шаблонов для выполнения вашей грязной работы, но это облегчит некоторые части вашей задачи.

0
ответ дан 18 December 2019 в 06:23
поделиться

Похоже, что вам нужна «Машина конечных состояний» , где с помощью этих случаев вы можете активировать различные процессы или «состояния». В C это обычно делается с помощью массива (матрицы) указателей на функции.

Таким образом, вы, по сути, создаете массив и помещаете правильные указатели функций в правильные указатели, а затем используете свою 'var' в качестве индекса справа ' процесс ', а затем вы его вызываете. Вы можете сделать это на большинстве языков. Таким образом, разные входы в машину активируют разные процессы и переводят ее в разные состояния. Это очень полезно для множества приложений; Я сам постоянно использую его при разработке MCU.

Edit: Валя указала, что мне, наверное, стоит показать базовую модель:

stateMachine[var1][var2]();   // calls the right 'process' for input var1, var2
13
ответ дан 18 December 2019 в 06:23
поделиться

Это неплохое решение, но, как всегда, есть альтернативы. Например, вы можете избавиться от оператора switch и использовать интерпретируемый обработчик для строк. Это похоже на список указателей на функции, но вам не нужно обновлять список, чтобы добавить новое поведение; об этом позаботится простое добавление новой функции в обработчик.

$array = array(
  "something" => "itsasecret",
  "somethingelse" => "i can't tell you",
);

class Handler {
  static function something($value) {
    printf("something: %s\n", $value);
  }

  static function somethingelse($value) {
    printf("somethingelse: %s\n", $value);
  }
}

$handler = new Handler();
foreach($array as $key => $value) {
  $handler->$key($value);
}

Возможно, вам понадобится код для очистки входных строк и обеспечения существования метода в вашем обработчике, но это может дать вам некоторые идеи.

да, возможно, проблема. По мере увеличения числа переменных и уровня начинается комбинаторный взрыв, а также формат оператора switch имеет тенденцию распространять точку ветвления и связанные с ним значения на длинном вертикальном участке. В этом случае таблица с 3 (или более) измерениями, инициализированная с помощью функции fct. указатели объединяют значения ветвления и функцию, которая должна быть вызвана, в одной строке.
  • Написание меньшего количества кода: извините, здесь мало помощи; в конце концов, нам нужно учитывать относительно большое количество комбинаций, и «карта», какой бы она ни была, должна быть где-то написана. Генераторы кода, такие как 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 ....
    }
    

    Случай, который может вызвать один НЕ с использованием вышеприведенного решения, - это , если комбинация пространство относительно мало заполнено (многие «ветви» в «дереве» переключателя не используются) или , если некоторые функции требуют другого набора параметров ; Для обоих этих случаев я хотел бы подключить решение, предложенное Джоэлом Гудвином первым здесь , которое по сути объединяет различные ключи для нескольких уровней в один более длинный ключ (с разделителем, если необходимо), по существу сглаживает проблему до длинного, но одноуровневого оператора переключения .

    Теперь ...

    Настоящая дискуссия должна быть о , зачем нам такое отображение / дерево решений в первую очередь . К сожалению, чтобы ответить на этот вопрос, необходимо понять истинную природу основного приложения. Конечно, я не говорю, что это признак плохого дизайна. В некоторых приложениях может иметь смысл большой раздел диспетчеризации. Однако даже с языком C (который, по-видимому, авторы французского сайта не допустили к объектно-ориентированному дизайну), можно принять объектно-ориентированную методологию и шаблоны. В любом случае, я расходлюсь ...) Возможно, что приложение в целом будет лучше обслуживаться с помощью альтернативных шаблонов проектирования, где «информационное дерево о том, что и когда вызывать» был распределен в нескольких модулях и / или нескольких объектах.

    Прошу прощения, если говорить об этом в довольно абстрактных терминах, это просто отсутствие специфики приложения ... Суть остается: оспорить идею о том, что нам нужно это большое дерево диспетчеризации; подумайте об альтернативных подходах к применению в целом.

    Alors, bonne Chance! ; -)

  • 5
    ответ дан 18 December 2019 в 06:23
    поделиться

    Идея указателя на функцию, вероятно, лучшая (согласно mjv, Shhnap). Но если код в каждом случае довольно мал, он может быть излишним и привести к большему запутыванию, чем предполагалось. В этом случае я мог бы реализовать что-нибудь быстрое и быстро читаемое, например следующее:

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

    Незнаком с вашим конкретным сценарием, поэтому, возможно, предыдущие предложения более подходят.

    1
    ответ дан 18 December 2019 в 06:23
    поделиться

    В зависимости от языка, некоторая форма хэш-карты с парой (var, subvar) в качестве ключа и первоклассными функциями в качестве значений (или независимо от вашего языка предлагает наилучшее приближение, например, экземпляры классов, расширяющих некоторый правильный интерфейс в Java), вероятно, обеспечат максимальную производительность - и полную лаконичность выборки соответствующей функции (или чего-то еще ;-) с карты на основе ключа и выполнения это обеспечивает удобство чтения для читателей, знакомых с языком и такими функциональными идиомами.

    4
    ответ дан 18 December 2019 в 06:23
    поделиться

    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;
    }
    
    1
    ответ дан 18 December 2019 в 06:23
    поделиться

    Да, определенно есть более простой способ сделать это, как быстрее , так и проще . Идея в основном такая же, как и у Алекса Мартелли. Вместо того, чтобы рассматривать вашу проблему как двумерную, рассматривайте ее как некую одномерную таблицу поиска.

    Это означает объединение переменных, подварок, подварок и т. Д. Для получения одного уникального ключа и использования его в качестве точки входа в вашу таблицу поиска.

    Способ сделать это зависит от используемого языка. С python, объединяющим var, subvar и т. Д., Чтобы построить кортеж и использовать его в качестве ключа в словаре, достаточно.

    С C или подобным обычно проще преобразовать каждый ключ в перечисления, а затем объединить их с помощью логических операторов, чтобы получить только один число, которое вы можете использовать в своем переключателе (это также простой способ использовать переключатель вместо сравнения строк с каскадными 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;
    }
    
    1
    ответ дан 18 December 2019 в 06:23
    поделиться

    Если C ++ - вариант, я бы попробовал использовать виртуальную функцию и, возможно, двойная отправка. Это могло бы сделать его намного чище. Но это, вероятно, окупится только в том случае, если у вас будет еще много дел.

    Эта статья на DDJ.com может быть хорошей записью.

    0
    ответ дан 18 December 2019 в 06:23
    поделиться

    Если вы просто пытаетесь исключить двухуровневые операторы 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;
    }
    
    0
    ответ дан 18 December 2019 в 06:23
    поделиться

    Если ваш язык - 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
    }
    
    0
    ответ дан 18 December 2019 в 06:23
    поделиться
    Другие вопросы по тегам:

    Похожие вопросы: