For循环和While循环之流的终结

开发 前端
循环语句是编程的基本组成部分。列表中的每一项都有用处,读取输入,直到输入结束,在屏幕上放置n个输入框。每当看到PR中的代码添加了循环语句,我都怒不可遏。现在我必定仔细检查代码,确保循环可以终止。

 本文转载自公众号“读芯术”(ID:AI_Discovery)

循环语句是编程的基本组成部分。列表中的每一项都有用处,读取输入,直到输入结束,在屏幕上放置n个输入框。每当看到PR中的代码添加了循环语句,我都怒不可遏。现在我必定仔细检查代码,确保循环可以终止。

[[389350]]

我希望所有运行良好的语句库中都看不到循环语句的踪影,但仍然有一些悄悄混进来,所以我想告诉大家如何消除循环语句。

让循环语句终结的关键是函数式编程。只需提供要在循环中执行的代码以及循环的参数(需要循环的内容)即可。我用Java作示范语言,但其实许多语言都支持这种类型的函数式编程,这种编程可以消除代码中的循环。

最简单的情况是对列表中的每个元素执行操作。

List<Integer> list = List.of(1, 2, 3); 
// bare for loop.  
for(int i : list) { 
   System.out.println("int = "+ i); 
}// controlled for each 
list.forEach(i -> System.out.println("int = " + i)); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

在这种最简单的情况下,无论哪种方法都没有太大优势。但第二种方法可以不使用bare for循环,而且语法更简洁。

我觉得forEach语句也有问题,应该只应用于副作用安全的方法。我所说的安全副作用是指不改变程序状态。上例只是记录日志,因此使用无碍。其他有关安全副作用的示例是写入文件、数据库或消息队列。

不安全的副作用会更改程序状态。下面为示例及其解决方法:

// bad side-effect, the loop alters sum 
int sum = 0; 
for(int i : list) { 
    sum += i; 

System.out.println("sum = " + sum);// no side-effect, sum iscalculated by loop 
sum = list 
       .stream() 
       .mapToInt(i -> i) 
       .sum(); 
System.out.println("sum = " + sum); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

另一个常见的例子:

// bad side-effect, the loop alters list2 
List<Integer> list2 = new ArrayList<>(); 
for(int i : list) { 
   list2.add(i); 

list2.forEach(i -> System.out.println("int = " + i));// no sideeffect, the second list is built by the loop 
list2 = list 
         .stream() 
         .collect(Collectors.toList()); 
list2.forEach(i -> System.out.println("int = " + i)); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

当你需要处理列表项方法中的索引时就会出现问题,但可以解决,如下:

// bare for loop with index
for(int i = 0; i < list.size(); i++) { 
    System.out.println("item atindex "  
      + i  
      + " = "  
      + list.get(i)); 
}// controlled loop with index
IntStream.range(0, list.size()) 
   .forEach(i ->System.out.println("item at index " 
    + i 
    + " = " 
    + list.get(i))); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

老生常谈的问题:读取文件中的每一行直到文件读取完毕如何解决?

BufferedReader reader = new BufferedReader( 
       new InputStreamReader( 
      LoopElimination.class.getResourceAsStream("/testfile.txt"))); 
// while loop with clumsy looking syntax 
String line; 
while((line = reader.readLine()) != null) { 
   System.out.println(line); 
}reader = new BufferedReader( 
       new InputStreamReader( 
      LoopElimination.class.getResourceAsStream("/testfile.txt"))); 
// less clumsy syntax 
reader.lines() 
   .forEach(l ->System.out.println(l)); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

应对上述情况有一个非常简便的lines方法,可以返回Stream类型。但是如果一个字符一个字符地读取呢?InputStream类没有返回Stream的方法。我们必须创建自己的Stream:

InputStream is = 
   LoopElimination.class.getResourceAsStream("/testfile.txt"); 
// while loop with clumsy looking syntax 
int c; 
while((c = is.read()) != -1) { 
  System.out.print((char)c); 

// But this is even uglier 
InputStream nis = 
   LoopElimination.class.getResourceAsStream("/testfile.txt"); 
// Exception handling makes functional programming ugly 
Stream.generate(() -> { 
   try { 
      return nis.read(); 
   } catch (IOException ex) { 
      throw new RuntimeException("Errorreading from file", ex); 
   } 
}) 
 .takeWhile(ch -> ch != -1) 
 .forEach(ch ->System.out.print((char)(int)ch)); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

这种情况下while循环看起来更好。此外,Stream版本还使用了可以返回无限项目流的 generate函数,因此必须进一步检查以确保生成过程终止,这是由于takeWhile方法的存在。

InputStream类存在问题,因为缺少peek 方法来创建可轻松转换为Stream的Iterator。它还会抛出一个检查过的异常,这样函数式编程就会杂乱无章。在这种情况下可以使用while语句让PR通过。

为了使上述问题更简洁,可以创建一个新的IterableInputStream类型,如下:

static class InputStreamIterable implements Iterable<Character> { 
  private final InputStream is
  public InputStreamIterable(InputStreamis) { 
    this.is = is
  } 
  public Iterator<Character>iterator() { 
     return newIterator<Character>() {               
        public boolean hasNext() { 
           try { 
             // poor man's peek: 
             is.mark(1); 
             boolean ret = is.read() !=-1; 
             is.reset(); 
             return ret; 
           } catch (IOException ex) { 
             throw new RuntimeException( 
                    "Error readinginput stream", ex); 
           } 
        } 
        public Character next() { 
           try { 
             return (char)is.read(); 
           } catch (IOException ex) { 
             throw new RuntimeException( 
                   "Error readinginput stream", ex); 
           } 
        } 
     }; 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

这样就大大简化了循环问题:

// use a predefined inputstream iterator: 
InputStreamIterable it = new InputStreamIterable( 
    LoopElimination.class.getResourceAsStream("/testfile.txt")); 
StreamSupport.stream(it.spliterator(), false
   .forEach(ch -> System.out.print(ch)); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

如果你经常遇到此类while循环,那么你可以创建并使用一个专门的Iterable类。但如果只用一次,就不必大费周章,这只是新旧Java不兼容的一个例子。

所以,下次你在代码中写for 语句或 while语句的时候,可以停下来思考一下如何用forEach 或 Stream更好地完成你的代码。

 

责任编辑:华轩 来源: 读芯术
相关推荐

2021-12-09 23:20:31

Python循环语句

2021-06-07 06:10:22

C++While循环For 循环

2024-02-26 12:13:32

C++开发编程

2009-07-21 14:03:00

Scalaif表达式while循环

2010-09-08 17:00:22

SQLWHILE循环

2023-02-25 16:33:12

Luawhile

2023-04-20 13:59:01

Pythonwhile循环的

2023-08-21 12:31:41

BashForWhile

2022-09-30 07:32:48

循环while循环体

2010-09-09 16:34:19

SQL循环while

2020-11-13 07:22:46

Java基础While

2022-01-27 09:35:45

whiledo-while循环Java基础

2020-12-11 05:57:01

Python循环语句代码

2010-01-07 15:42:57

VB.NET WhilEnd While循环

2021-03-17 11:16:58

while(1)for(;;)语言

2021-01-28 09:55:50

while(1)for(;;)Linux

2010-03-19 14:18:07

Java Socket

2024-11-08 16:13:43

Python开发

2022-10-28 07:38:06

Javawhile循环

2009-11-10 11:30:12

VB.NET循环语句
点赞
收藏

51CTO技术栈公众号