Избавиться от некрасивых высказываний - PullRequest
60 голосов
/ 24 сентября 2010

У меня есть этот уродливый код:

if ( v > 10 ) size = 6;
if ( v > 22 ) size = 5;
if ( v > 51 ) size = 4;
if ( v > 68 ) size = 3;
if ( v > 117 ) size = 2;
if ( v > 145 ) size = 1;
return size;

Как мне избавиться от нескольких операторов if?

Ответы [ 25 ]

160 голосов
/ 24 сентября 2010

Как насчет такого подхода:

int getSize(int v) {
    int[] thresholds = {145, 117, 68, 51, 22, 10};

    for (int i = 0; i < thresholds.length; i++) {
        if (v > thresholds[i]) return i+1;
    }
    return 1;
}

Функционально: (продемонстрировано в Scala)

def getSize(v: Int): Int = {
  val thresholds = Vector(145, 117, 68, 51, 22, 10)
  thresholds.zipWithIndex.find(v > _._1).map(_._2).getOrElse(0) + 1
}
88 голосов
/ 24 сентября 2010

Использование NavigableMap API:

NavigableMap<Integer, Integer> s = new TreeMap<Integer, Integer>();
s.put(10, 6);
s.put(22, 5);
s.put(51, 4);
s.put(68, 3);
s.put(117, 2);
s.put(145, 1);

return s.lowerEntry(v).getValue();
81 голосов
/ 28 сентября 2010

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

size = round(k_0 + k_1 * v + k_2 * v^2 + ...)

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

Для полинома с 45 степенями со следующими коэффициентами,

-9.1504e-91 1.1986e-87 -5.8366e-85 1.1130e-82 -2.8724e-81 3.3401e-78 -3.3185e-75  9.4624e-73 -1.1591e-70 4.1474e-69 3.7433e-67 2.2460e-65 -6.2386e-62 2.9843e-59 -7.7533e-57 7.7714e-55 1.1791e-52 -2.2370e-50 -4.7642e-48 3.3892e-46 3.8656e-43 -6.0030e-41 9.4243e-41 -1.9050e-36 8.3042e-34 -6.2687e-32 -1.6659e-29 3.0013e-27 1.5633e-25 -8.7156e-23  6.3913e-21 1.0435e-18 -3.0354e-16 3.8195e-14 -3.1282e-12 1.8382e-10 -8.0482e-09 2.6660e-07 -6.6944e-06 1.2605e-04 -1.7321e-03 1.6538e-02 -1.0173e-01 8.3042e-34 -6.2687e-32 -1.6659e-29 3.0013e-27 1.5633e-25 -8.7156e-23 6.3913e-21 1.0435e-18 -3.0354e-16 3.8195e-14 -3.1282e-12 1.8382e-10 -8.0482e-09 2.6660e-07 -6.6944e-06 1.2605e-04 -1.7321e-03 1.6538e-02 -1.0173e-01 3.6100e-01 -6.2117e-01 6.3657e+00

, вы получите прекрасно подогнанную кривую:

alt text

И, как вы можете видеть, вы получаете ошибку 1 нормы всего 1,73 во всем диапазоне от 0 до 200 *!

* Результаты для v∉[0,200] могут отличаться.

76 голосов
/ 24 сентября 2010
if ( v > 145 ) size = 1;
else if ( v > 117 ) size = 2;
else if ( v > 68 ) size = 3;
else if ( v > 51 ) size = 4;
else if ( v > 22 ) size = 5;
else if ( v > 10 ) size = 6;

return size;     

Это лучше для вашего случая.

По желанию вы можете выбрать Switch Case, когда это возможно

Update: Если вы проанализировали значение 'v'как правило, в большинстве случаев находится в более низком диапазоне (<10), чем вы можете добавить это. </strong>

if(v < 10)           size = SOME_DEFAULT_VALUE;
else if ( v > 145 )  size = 1;
else if ( v > 117 )  size = 2;
else if ( v > 68 )   size = 3;
else if ( v > 51 )   size = 4;
else if ( v > 22 )   size = 5;
else if ( v > 10 )   size = 6;   

further : Вы также можете изменить последовательность условий в соответствии с вашим анализом.Если вы знаете, что большинство значений меньше 10, а затем, во-вторых, большинство значений лежат в диапазоне 68-117, вы можете соответствующим образом изменить последовательность условий.

Правки:

if(v < 10)           return SOME_DEFAULT_VALUE;
else if ( v > 145 )  return 1;
else if ( v > 117 )  return 2;
else if ( v > 68 )   return 3;
else if ( v > 51 )   return 4;
else if ( v > 22 )   return 5;
else if ( v > 10 )   return 6;   
51 голосов
/ 24 сентября 2010
return v > 145 ? 1 
     : v > 117 ? 2 
     : v > 68 ? 3 
     : v > 51 ? 4 
     : v > 22 ? 5 
     : v > 10 ? 6 
     : "put inital size value here";
23 голосов
/ 25 сентября 2010

Оригинальный код кажется мне подходящим, но если вы не возражаете против множественных возвратов, вы можете предпочесть более табличный подход:

if ( v > 145 ) return 1;
if ( v > 117 ) return 2;
if ( v >  68 ) return 3;
if ( v >  51 ) return 4;
if ( v >  22 ) return 5;
if ( v >  10 ) return 6;
return ...;     // The <= 10 case isn't handled in the original code snippet. 

Смотрите обсуждение множественных возвратов или нет в orglife.java ответ .

17 голосов
/ 24 сентября 2010

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

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

14 голосов
/ 25 сентября 2010
return (v-173) / -27;
12 голосов
/ 24 сентября 2010

Вот мой выстрел в это ...

Обновление: исправлено. Предыдущее решение дало неправильные ответы на точные значения (10,22,51 ...). Это значение по умолчанию 6 для if val <10 </em>

   static int Foo(int val)
    {
                          //6, 5, 4, 3, 2 ,1
        int[] v = new int[]{10,22,51,68,117,145};
        int pos = Arrays.binarySearch(v, val-1);
        if ( pos < 0) pos = ~pos;
        if ( pos > 0) pos --;
        return 6-pos;
    }
11 голосов
/ 27 сентября 2010

У меня есть еще одна версия для вас. Я не думаю, что это лучший вариант, потому что он добавляет ненужную сложность во имя «производительности», когда я на 100% уверен, что эта функция никогда не приведет к снижению производительности (если кто-то не вычислит размер в узком цикле миллион раз ...).

Но я представляю это только потому, что Я думал, что выполнение жестко запрограммированного двоичного поиска было бы довольно интересно . Он выглядит не очень бинарно, потому что не хватает элементов, чтобы углубиться, но он обладает тем преимуществом, что возвращает результат не более чем в 3 тестах, а не в 6 , как в оригинальный пост. Операторы возврата также упорядочены по размеру, что поможет понять и / или изменить их.

if (v > 68) {
   if (v > 145) {
      return 1
   } else if (v > 117) {
      return 2;
   } else {
      return 3;
   }
} else {
   if (v > 51) {
      return 4;
   } else if (v > 22) {
      return 5;
   } else {
      return 6;
   }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...