回归基础:if,for和switch之外的事情

[原文发表地址]  Back to Basics: Moving beyond for, if and switch

[原文发表时间] 2012-04-26 19:59

我访问了很多的客户,查看了大量的代码。我还在我以前的工作中接触到了很多大型产品代码基地,我看到了大量的 if、 for和switch语句。我看到循环之中还嵌套着循环,其中if做着各种数据转换,从一种格式转换为另一种。我看到解析字符串来获取数据,用英文来表达这个意思很简单,但如果是编写代码的话,就需要100 行。

当我们刚起步编程时,我们就首先学习if,接着是for,然后是滥用 switch 语句。

几个星期前我在Miguel的博客上看到这片小小的代码段:

    1: var biggerThan10 = new List;
    2: for (int i = 0; i < array.Length; i++){
    3:     if (array [i] > 10)
    4:        biggerThan10.Add (array[i]);
    5: }

它很简单。排列一组整数数组,然后将那些比 10 大的数字做一个新的列表。我们已经百万次看到类似这样的代码。这里是其他几种语言所编写的同一事情。

C#

    1: var a = from x in array where x > 10 select x;
    2: var b = array.Where(x => x > 10);

Ruby

    1: a = array.select{|x| x >10}

JavaScript

    1: a = array.filter(function(x){return x > 10}); 

比起上述的循环和if,我更喜欢编写这些一行操作的代码。我在世界上看到了很多,所以或许人们没有看见足够多的例子。我要求请求 Twitter 上的朋友提交他们的例子。谢谢你们,Twitter 朋友们 !

这里有几个很好的例子。Iron Shay在他的博客中有一些不错的 LINQ示例请在评论中分享你的例子。 请务必使用 <pre> 标记。

说明: 这是关于"将事情简化为一行",同样的解决方案,同等的可读性,它们比循环更简单、 简洁和少出错。

    1: def calculate_primes(n):
    2:     no_primes = []
    3:     primes = []
    4:         for i in range(2, 8):
    5:         for j in range(i*2, n, i):
    6:             no_primes.append(j)
    7:         for x in range(2, n):
    8:             if x not in no_primes:
    9:             primes.append(x)
   10:         return primes
   11:  
   12: calculate_primes(500)
   13: # Can be like this instead!
   14: (lambda n: [x for x in range(2, n) if x not in [j for i in range(2, 8) for j in range(i*2, n, i)]])(500)

来自Aaron Bassett

    1: foreach (var i in categories) {
    2:      foreach (var x in GetAllChildCategories(i.Id)) {
    3:            yield return x;
    4:      }
    5: }
    6:  
    7:  //Can be... return categories.SelectMany(i => this.GetAllChildCategoriesIds(i.Id));

来自James Hull

    1: var inputNumbersInString = Console.ReadLine();
    2: var inputNumbersStringArray = inputNumbersInString.Split(' ');
    3: var inputNumbers = new List<int>();
    4:  
    5:  for (int i = 0; i < inputNumbersStringArray.Length; ++i) {
    6:     inputNumbers.Add(int.Parse(inputNumbersStringArray[i]));
    7: }
    8:  
    9:  int maxNumber = inputNumbers[0];
   10:  
   11:  for (int i = 1; i < inputNumbers.Count; ++i)
   12:     if (inputNumbers[i] > maxNumber)
   13:         maxNumber = inputNumbers[i];
   14:  
   15:  Console.WriteLine(maxNumber);
   16:  
   17: //Or rather...
   18:  
   19:  Console.WriteLine(Console.ReadLine().Split(' ').Select(t => int.Parse(t)).ToList().Max());

来自Amit Saraswat

    1: // create a poker deck as a list of two characters strings:
    2: // rank, suite
    3:  
    4: char[] figures = "23456789TJQKA".ToCharArray();
    5: char[] suites = "SHDC".ToCharArray();
    6: List<string> deck = new List<string>();
    7:  
    8:  foreach (var figure in figures) {
    9:     foreach (var suite in suites) {
   10:         deck.Add(string.Format("{0}{1}", figure, suite));
   11:     }
   12:
   13: }
   14:  //Or, neatly
   15: var cards = from r in "23456789TJQKA" from s in "SHDC" select "" + r + s;

来自Jack Nova

    1: bool include = false;
    2: if (op == Operator.And) {
    3:     bool current = true;
    4:     foreach (var item in Items) {
    5:         current = current & item.Process();
    6:     }
    7:     include = current;
    8: }
    9: else {
   10:     bool current = false;
   11:     foreach (var item in Items) {
   12:         current = current | item.Process();
   13:     }
   14:     include = current;
   15: }
   16: return include;
   17:  
   18:  //Or this lovely Aggregate
   19:  
   20:  return op == Operator.And ?
   21:  Items.Aggregate(true, (current, item) => current & item.Process()) :
   22:  Items.Aggregate(false, (current, item) => current | item.Process()); 

来自Kevin Meiresonne

    1: sbyte[] sByteArray = new sbyte[100];
    2: byte[] uByteArray = new byte[sByteArray.Length];
    3:  
    4:  for (int i = 0; i < sByteArray.Length; i++) {
    5:     uByteArray[i] = (byte)sByteArray[i];
    6: }
    7:  
    8:  //Or, instead of the loop above
    9: byte[] uByteArray1 = Array.ConvertAll(sByteArray, x => (byte)x);

来自Fahad Mustafa

Scott:在这里我不得说我更喜欢第一个选项。;)

    1: // This is the "classic" solution to the FizzBuzz problem.
    3:     if (i % 3 == 0 && i % 5 == 0)     {
    4:         Console.WriteLine("FizzBuzz");
    2: for (int i = 1; i <= 100; i++) {
    5:     }
    6:     else if (i % 3 == 0)     {
    7:         Console.WriteLine("Fizz");
    8:     }
    9:     else if (i % 5 == 0) {
   10:         Console.WriteLine("Buzz");
   11:     }
   12:     else {
   13:         Console.WriteLine(i.ToString());
   14:     }
   15: }
   16:  
   17:  // One line
   18:  Enumerable.Range(1, 100).ToList().ForEach(n => Console.WriteLine((n % 3 == 0) ? (n % 5 == 0) ? "FizzBuzz":"Fizz" : (n % 5 == 0) ? "Buzz" : n.ToString()));

来自Rudi Larno

一个好的代码段实现手段 ...我感到惊讶的是更多的人不使用此。

    1: var temp = String.Empty;
    2: foreach (var entry in myStringList) {
    3:     if (String.IsNullOrEmpty(temp)) {
    4:         temp = entry;
    5:     }
    6:     else {
    7:         entry += ", " + entry;
    8:     }
    9: }
   10:  
   11: //becomes
   12:  
   13: var temp = String.Join(", ", myStringList)

来自Holger Adam

用一行F#编写的带有属性的一个类。这将要用十行甚至更多行的C#代码来编写。

    1: type Person = { Name:string; Age:int }

来自Phillip Trelford

    1: /// Input is a string with numbers : 10+20+30+40
    2: /// Output is integer with required sum (100)
    3: string input = "10+20+30+40";
    4: var result = Regex.Split(input, @"\D+").Select(t => int.Parse(t)).Sum();
    5: Console.WriteLine("Result is {0}" ,result);

来自Srinivas Iyengar

对于程序人员来说,除了我们所学的前三个关键字,还有很多东西可用于编程。您最喜欢的能帮助你摆脱基础知识,移到下一个级别的模式是什么 (不管是什么语言)?