代码之家  ›  专栏  ›  技术社区  ›  kofucii

摆脱丑陋的if语句

  •  60
  • kofucii  · 技术社区  · 14 年前

    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 回复  |  直到 14 年前
        1
  •  75
  •   Jigar Joshi    11 年前
    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;     
    

    这对你的案子更好。

    如果可能的话,也可以选择开关箱

    Update:

    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
  •  160
  •   missingfaktor Kevin Wright    13 年前

    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
  •  88
  •   barjak    14 年前

    使用 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
  •  82
  •   Alexander Torstling    14 年前

    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

    如你所见,在0到200*的整个范围内,你得到的1范数误差只有1.73!

    *的结果 v∉[0,200] 可能会有所不同。

        5
  •  51
  •   BalusC    14 年前
    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
  •  23
  •   Community CDub    7 年前

    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's answer .

        7
  •  17
  •   cbmeeks    14 年前

    如果您有几十个或几百个迭代要检查,那么我可以很容易地看到转到一些for循环,但老实说,对于您所做的少数比较,请坚持使用If,然后继续。没那么难看。

        8
  •  14
  •   mhaller    14 年前
    return (v-173) / -27;
    
        9
  •  12
  •   st0le    14 年前

    更新:已修复。先前的解决方案给出了精确值(10,22,51…)的错误答案。对于if val<10,此值默认为6

       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
  •  11
  •   momo    14 年前

    我还有一个版本给你。我真的不认为它是最好的,因为它以“性能”的名义增加了不必要的复杂性,而我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;
       }
    }
    
        11
  •  7
  •   George    7 年前
    7 - (x>10 + x>22 + x>51 + x>68 + x>117 + x>145)
    

    哪里 7 是默认值( x <= 10

    编辑:起初我不知道这个问题是关于Java的。此表达式在java中无效,但在C/C++中是有效的。我将留下答案,因为有些用户觉得它很有用。

        12
  •  5
  •   Chris Adragna    14 年前

    我的评论能力还没有打开,希望没有人会说“正确”的基础上,我的答案。。。

    美化丑陋的代码可以/应该被定义为试图实现:

    1. 可读性(好的,说明显而易见的——也许是多余的问题)
    2. 实用主义——这与大多数人的做事方式并不遥远,考虑到一个不需要优雅或独特解决方案的普通问题,以后改变它应该是一种自然的努力,不需要太多的回忆。

    综观所有关于这个问题的评论,在我写作的时候,似乎只有org.life.java网站提出了性能的问题(也许mfloryan也提出了一些“更长”的问题)。当然,在大多数情况下,举这个例子,不管你怎么写它,它都不应该承受明显的减速。

    但是,通过嵌套条件并对条件进行优化排序,可以提高性能[值得一提的是,特别是在循环的情况下]。

        13
  •  5
  •   Sean Patrick Floyd    14 年前

    这是一个面向对象的解决方案,一个名为 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
                )
            ];
        }
    }
    

    Edit:最终用一个更高效(更短)的版本替换了map()方法。我知道:一个搜索分区的版本对于大型数组来说仍然会更快,但是对不起:我太懒了。

    1. 它包含一个生成器,允许您使用varargs语法创建映射器。我认为这是可用性的必备条件
    2. 它同时包含单个项和集合映射方法
    3. 它是不可变的,因此是线程安全的

    当然,所有这些特性都可以很容易地删除,但是代码将不那么完整、不太可用或不太稳定。

        14
  •  4
  •   user207421    14 年前

    有没有一个基本的数学规则呢?如果是这样的话,你应该使用它:但前提是它来自于问题领域,而不仅仅是一些恰好适合案例的公式。

        15
  •  3
  •   Ani    14 年前
    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
  •  3
  •   Sarev of Aona    7 年前

    你可以用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
  •  2
  •   momo    14 年前

    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
    
        18
  •  1
  •   phv3773    14 年前

    请原谅我没有把整件事都写出来。 当然,你得确保v在射程内。

    我能想到这样做的唯一原因是,如果你要创建一次数组,并在一个必须非常快速的应用程序中使用它数千次。我提到它是一个在内存和速度(不是以前的问题)以及设置时间和速度之间进行权衡的例子。

        19
  •  1
  •   Zutty    14 年前

    显而易见的答案是使用Groovy:

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

    一句话总是好的。对于v<=10的未定义情况,返回7。

        20
  •  0
  •   CashCow    14 年前

    这是我的代码示例,使用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;
    
        21
  •  0
  •   Adrian M    14 年前

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

    它现在变得更加灵活,可以按降序处理任何给定的数组,并且该方法将找到值“v”所属的索引。

        22
  •  0
  •   CashCow    14 年前

    如果你真的想用最快的大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
  •  0
  •   RTA    11 年前

    为什么有人不建议转换语句。如果不是梯子,那就好得多。

    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;
        }
    
        24
  •  -1
  •   Vikram    12 年前
                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);
                }
    

        25
  •  -1
  •   Community CDub    7 年前

    还有另一种变化(比 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;