摆脱丑陋的陈述

我有这个丑陋的代码:

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语句?

 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; 

这对你的情况更好。

可选的,你应该尽可能select开关盒

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; 

这样的方法如何:

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

function上:(在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 } 

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

OPs解决scheme最明显的问题是分支,所以我会build议一个多项式回归。 这将在窗体上产生一个很好的无分式expression式

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

你当然不会得到一个确切的结果,但如果你能容忍一些偏差,这是一个非常高性能的select。 由于对于v<10不可能用多项式进行build模的值来说,将“未修改”的行为保留为原始函数,所以我冒昧地假定该区域的零阶保持插值。

对于具有以下系数的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 

,你会得到一个精美的曲线:

替代文字

正如你所看到的,在0到200 *的整个范围内,你会得到1.73的1范数误差。

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

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

原始代码对我来说似乎很好,但如果你不介意多个返回,你可能更喜欢更多的表格方法:

 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的回答中查看多重返回或不讨论。

这里有很多的答案和build议,但我并不认为它们比原来的方法“更漂亮”或“更优雅”。

如果你有几十次或几百次的迭代检查,那么我可以很容易地看到一些循环,但老实说,为了less量的比较,坚持下去,继续前进。 这不是那么丑。

 return (v-173) / -27; 

这是我的拍摄…

更新:修正。 以前的解决scheme给出了准确值的不正确答案(10,22,51 …)。 如果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; } 

我有一个更多的版本给你。 我真的不认为这是最好的,因为当我100%确定这个函数永远不会是一个performance猪(除非有人在一个紧密的循环中计算大小一百万次…)。

但我只是因为我认为执行一个硬编码的二进制search是有趣的 。 它看起来并不是很“二元化”,因为没有足够的元素去深入,但是它具有的优点是它返回的结果不超过3个testing,而不是原来的post。 报税表的大小顺序也有助于理解和/或修改。

 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; } } 
 7 - (x>10 + x>22 + x>51 + x>68 + x>117 + x>145) 

其中7是默认值( x <= 10 )。

编辑:最初我没有意识到这个问题是关于Java。 此expression式在Java中无效,但在C / C ++中有效。 我会留下答案,因为有些用户觉得有帮助。

我的评论能力还没有打开,希望没有人会根据我的回答“正确地”说。

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

  1. 可读性(好吧,说明问题或许是显而易见的)
  2. 性能 – 最好是寻求最佳,在最坏的情况下,这不是一个大的消耗
  3. 实用主义 – 与大多数人所做的事情相差不远,考虑到一个普通的问题,不需要一个优雅或独特的解决scheme,以后改变它应该是一个自然的努力,而不需要太多的回忆。

国际海事组织由org.life.java给出的答案是最漂亮的,非常容易阅读。 由于阅读和表演的原因,我也喜欢写条件的顺序。

回顾一下关于这个主题的所有评论,在我写这篇文章的时候,似乎只有org.life.java提出了性能问题(也许还有一些说法是“更长”)。 当然,在大多数情况下,考虑到这个例子,不pipe你写的是什么,它都不应该承受明显的放缓。

然而,通过嵌套你的条件和最佳的条件sorting可以提高性能[值得,特别是如果这是环状]。

所有这一切,嵌套和订购条件(比你的例子更复杂)带来的决心,以尽可能快的执行,往往会产生较less的可读代码,而代码是很难改变。 我再次提到#3,实用主义……平衡需求。

这是一个面向对象的解决scheme,一个名为Mapper<S,T>的类Mapper<S,T>它映射来自任何types的值,这些types实现了与任何目标types相媲美的值。

句法:

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

编辑:最后用一个更有效的(和更短的)版本replacemap()方法。 我知道:search分区的版本对于大型数组来说仍然会更快,但对不起:我太懒了。

如果你认为这太臃肿,考虑一下:

  1. 它包含一个构build器,可让您使用varargs语法创build映射器。 我会说这是可用性的必要条件
  2. 它包含单个项目和集合映射方法
  3. 它是不可变的,因此线程安全

当然,所有这些function都可以很容易地删除,但是代码不够完整,可用性较差或者不太稳定。

是否有一个基本的math规则呢? 如果是这样,你应该使用它:但只有当它来自问题领域,而不仅仅是适合案件的一些公式。

 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; 

为了完整性,让我build议你可以用145个元素来设置一个数组SIZES,所以答案可以直接作为SIZES [v]返回。 请原谅我不把整件事写出来。 当然,你必须确保v在范围内。

我能想到的唯一理由就是如果你要创build一个数组,并在成千上万次的应用程序中使用它,必须非常快速。 我提到它是内存和速度之间的平衡(不是曾经的问题),也是build立时间和速度之间的一个折中的例子。

实际上,如果大小可能改变,那么在数据库中做这个可能是一个很好的替代策略:

 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 

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

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

一条衬里总是更好。 对于v <= 10的未定义的情况返回7。

为什么有人没有build议切换语句。 如果其他阶梯好得多。

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

这是我的代码示例,使用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; 

有趣的是,一个简单的“丑陋”的问题有很多美丽的答案。 我最喜欢mfloryan的答案,但是我会通过删除方法内部的硬编码数组来进一步推动它。 就像是,

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

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

PS。 我还不能评论答案。

如果你真的想要这个特定的答案最快的大O复杂时间解决scheme这一个是不断查找。

 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的所有可能的结果标记在范围内以及它们落在哪里,然后我们只需要testing边界条件。

这个问题是,它使用更多的内存,当然,如果maxBoundary更大,这将是非常低效率的空间(以及需要更长的时间来初始化)。

这有时可能是最好的解决scheme。

  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语句。

还有另外一个变化( 比乔治的答案更不明显)

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