Подтвердить что ты не робот

Избавьтесь от уродливых утверждений

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

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;

Как я могу избавиться от операторов multiple if?

4b9b3361

Ответ 1

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), чем вы можете добавить это.

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;   

Ответ 2

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

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
}

Ответ 3

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

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

Ответ 4

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

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] могут отличаться.

Ответ 5

return v > 145 ? 1 
     : v > 117 ? 2 
     : v > 68 ? 3 
     : v > 51 ? 4 
     : v > 22 ? 5 
     : v > 10 ? 6 
     : "put inital size value here";

Ответ 6

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

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. 

См. множественное возвращение или отсутствие обсуждения в сообщении org.life.java.

Ответ 7

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

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

Ответ 8

return (v-173) / -27;

Ответ 9

Здесь мой снимок...

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

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

Ответ 10

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

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

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

Ответ 11

7 - (x>10 + x>22 + x>51 + x>68 + x>117 + x>145)

где 7 - значение по умолчанию (x <= 10).

Изменить: первоначально я не понимал, что этот вопрос касается Java. Это выражение недопустимо в Java, но оно действительно в C/С++. Я оставлю ответ, поскольку некоторые пользователи считают его полезным.

Ответ 12

Вот объектно-ориентированное решение, класс под названием Mapper<S,T>, который отображает значения любого типа, реализующего сопоставимые с любым типом цели.

Синтаксис:

Mapper<String, Integer> mapper = Mapper.from("a","b","c").to(1,2,3);

// Map a single value
System.out.println(mapper.map("beef")); // 2

// Map a Collection of values
System.out.println(mapper.mapAll(
    Arrays.asList("apples","beef","lobster"))); // [1, 2, 3]

Код:

public class Mapper<S extends Comparable<S>, T> {

    private final S[] source;
    private final T[] target;

    // Builder to enable from... to... syntax and
    // to make Mapper immutable
    public static class Builder<S2 extends Comparable<S2>> {
        private final S2[] data;
        private Builder(final S2[] data){
            this.data = data;
        }
        public <T2> Mapper<S2, T2> to(final T2... target){
            return new Mapper<S2, T2>(this.data, target);
        }
    }


    private Mapper(final S[] source, final T[] target){
        final S[] copy = Arrays.copyOf(source, source.length);
        Arrays.sort(copy);
        this.source = copy;
        this.target = Arrays.copyOf(target, target.length);
    }

    // Factory method to get builder
    public static <U extends Comparable<U>, V> Builder<U> from(final U... items){
        return new Builder<U>(items);
    }

    // Map a collection of items
    public Collection<T> mapAll(final Collection<? extends S> input){
        final Collection<T> output = new ArrayList<T>(input.size());
        for(final S s : input){
            output.add(this.map(s));
        }
        return output;
    }

    // map a single item
    public T map(final S input){
        final int sourceOffset = Arrays.binarySearch(this.source, input);
        return this.target[
            Math.min(
                this.target.length-1,
                sourceOffset < 0 ? Math.abs(sourceOffset)-2:sourceOffset
            )
        ];
    }
}

Изменить: наконец, заменил метод map() более эффективной (и более короткой) версией. Я знаю: версия, которая ищет разделы, по-прежнему будет быстрее для больших массивов, но извините: я слишком ленив.

Если вы считаете, что это слишком раздутое, подумайте об этом:

  • Он содержит построитель, который позволяет создавать Mapper с помощью синтаксиса varargs. Я бы сказал, что обязательно для удобства использования
  • Он содержит как один элемент, так и метод сопоставления коллекции
  • Это неизменный и, следовательно, безопасный поток

Конечно, все эти функции могут быть легко удалены, но код будет менее полным, менее полезным или менее стабильным.

Ответ 13

Моя способность комментировать не включена, надеюсь, никто не скажет "по праву" на основе моего ответа...

Удовлетворение уродливого кода может/должно определяться как попытка достижения:

  • Считываемость (ОК, с указанием очевидного - излишнего вопроса)
  • Производительность - в лучшем случае оптимальная, в худшем случае это не большой утечек.
  • Прагматизм - это далеко не так, как делают большинство людей, учитывая обычную проблему, которая не нуждается в изящном или уникальном решении, изменяя его позже, должна быть естественным усилием, не нуждающимся в большом воспоминании.

ИМО ответ, данный org.life.java, был самым красивым и чрезвычайно легким для чтения. Мне также понравился порядок, в котором условия были написаны по причинам чтения и исполнения.

Оглядываясь на все комментарии по этому вопросу, на момент написания статьи выяснилось, что только org.life.java поднял вопрос о производительности (и, возможно, mfloryan тоже заявляет, что что-то будет "дольше" ). Конечно, в большинстве ситуаций, и, учитывая этот пример, он не должен иметь заметного замедления, но вы его пишете.

Однако, вложенные ваши условия и оптимальный порядок условий могут повысить производительность [стоит, особенно если это было зациклировано).

Все, что сказано, условия вложения и упорядочения (более сложные, чем ваш пример), полученные путем определения для достижения как можно более быстрого выполнения, часто создают менее читаемый код и код, который сложнее изменить. Я снова ссылаюсь на № 3, прагматизм... уравновешивающий потребности.

Ответ 14

Существует ли для этого основное математическое правило? Если это так, вы должны использовать это: но только если он исходит из проблемного домена, а не только какая-то формула, которая подходит для случаев.

Ответ 15

int[] arr = new int[] {145, 117, 68, 51, 22, 10};
for(int index = 0; index < arr.length; index++)
{
  if(v > arr[index]) return 1 + index; 
}

return defaultValue;

Ответ 16

Вы можете переписать его в коде ARM. Это всего лишь 7 циклов худшего случая и тонкие 164 байта. Надеюсь, это поможет. (обратите внимание: это не проверено)

; On entry
;   r0 - undefined
;   r1 - value to test
;   lr - return address
; On exit
;   r0 - new value or preserved
;   r1 - corrupted
;
wtf
        SUBS    r1, r1, #10
        MOVLE   pc, lr
        CMP     r1, #135
        MOVGT   r0, #1
        ADRLE   r0, lut
        LDRLEB  r0, [r0, r1]
        MOV     pc, lr
;
; Look-up-table
lut
        DCB     0   ; padding
        DCB     6   ; r1 = 11 on entry
        DCB     6
        DCB     6
        DCB     6
        DCB     6
        DCB     6
        DCB     6
        DCB     6
        DCB     6
        DCB     6
        DCB     6
        DCB     6
        DCB     5   ; r1 = 23 on entry
        DCB     5
        ...
        ALIGN

Ответ 17

Просто для полноты позвольте предложить вам установить массив SIZES с 145 элементами, чтобы ответ мог быть возвращен непосредственно как SIZES [v]. Простите меня за то, что я не написал все это. Конечно, вы должны были убедиться, что v находится в зоне действия.

Единственная причина, по которой я могу думать о том, чтобы сделать это так, была бы, если бы вы собирались создать массив один раз и использовать его тысячи раз в приложении, которое должно было быть действительно быстрым. Я упоминаю это как пример компромисса между памятью и скоростью (а не той проблемой, которая когда-то была), а также между временем установки и скоростью.

Ответ 18

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

CREATE TABLE VSize (
   LowerBound int NOT NULL CONSTRAINT PK_VSize PRIMARY KEY CLUSTERED,
   Size int NOT NULL
)
INSERT VSize VALUES (10, 6)
INSERT VSize VALUES (22, 5)
INSERT VSize VALUES (51, 4)
INSERT VSize VALUES (68, 3)
INSERT VSize VALUES (117, 2)
INSERT VSize VALUES (145, 1)

И хранимая процедура или функция:

CREATE PROCEDURE VSizeLookup
   @V int,
   @Size int OUT
AS
SELECT TOP 1 @Size = Size
FROM VSize
WHERE @V > LowerBound
ORDER BY LowerBound

Ответ 19

Очевидным ответом является использование Groovy:

def size = { v -> [145,117,68,51,22,10].inject(1) { s, t -> v > t ? s : s + 1 } }

Один лайнер всегда лучше. Возвращает 7 для случая undefined, где v <= 10.

Ответ 20

почему кто-то не предложил оператор switch. это намного лучше, если бы была другая лестница.

public int getSize(int input)
    {
        int size = 0;
        switch(input)
        {
        case 10:
            size = 6;
            break;

        case 22:
            size = 5;
            break;


        case 51:
            size = 4;
            break;

        case 68:
            size = 3;
            break;

        case 117:
            size = 2;
            break;

        case 145:
            size = 1;
            break;
        }

        return size;
    }

Ответ 21

Это мой пример кода, используя SortedSet. Вы инициализируете границы один раз.

SortedSet<Integer> boundaries = new SortedSet<Integer>;

boundaries.add(10);

boundaries.add(22);

boundaries.add(51);

boundaries.add(68);

boundaries.add(117);

boundaries.add(145);

Затем используйте его впоследствии для нескольких значений v (и инициализированного размера)

SortedSet<Integer> subset =  boundaries.tailSet(v);
if( subset.size() != boundaries.size() )
  size = subset.size() + 1;

Ответ 22

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

final int minBoundary = 10;
final int maxBoundary = 145;
final int maxSize = 6;
Vector<Integer> index = new Vector<Integer>(maxBoundary);
    // run through once and set the values in your index

затем

if( v > minBoundary )
{
   size = (v > maxBoundary ) ? maxSize : index[v];
}

То, что мы здесь делаем, обозначает все возможные результаты v в пределах диапазона и где они падают, а затем нам нужно только проверить граничные условия.

Проблема заключается в том, что он использует больше памяти и, конечно, если maxBoundary намного больше, он будет очень неэффективным (а также потребуется больше времени для инициализации).

Иногда это может быть лучшим решением ситуации.

Ответ 23

Интересно, что есть много прекрасных ответов на простой "уродливый" вопрос. Мне нравится mfloryan answer best, однако я бы нажал его дальше, удалив жестко закодированный массив внутри метода. Что-то вроде,

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

Теперь он становится более гибким и может обрабатывать любой заданный массив в порядке убывания, и метод найдет индекс, где принадлежит значение 'v'.

PS. Я еще не могу прокомментировать ответы.

Ответ 24

            if (v <= 10)
                return size;
            else {
                size = 1;

                if (v > 145)
                    return size;
                else if (v > 117)
                    return ++size;
                else if (v > 68)
                    return (size+2);
                else if (v > 51)
                    return (size+3);
                else if (v > 22)
                    return (size+4);
                else if (v > 10)
                    return (size+5);
            }

Это приведет к выполнению только необходимых операторов if.

Ответ 25

Еще одна вариация (менее выраженная, чем ответ George)

  //int v = 9;
  int[] arr = {145, 117, 68, 51, 22, 10};
  int size = 7; for(;7 - size < arr.length && v - arr[size - 2] > 0; size--) {};
  return size;