当前位置: 首页 > 科技观察

For循环和While循环的终结

时间:2023-03-20 13:11:45 科技观察

本文转载自公众号《阅读核心》(ID:AI_Discovery)循环语句是编程的基础部分。列表中的每一项都是有用的,读取输入直到输入结束,并在屏幕上放置n个输入框。每次我在PR中看到添加循环语句的代码时,我都会很生气。现在我必须仔细检查代码以确保循环可以终止。我希望在任何行为良好的库中看到没有循环,但仍然有一些潜入,所以我想向您展示如何消除它们。让循环终止的关键是函数式编程。只需提供循环中要执行的代码和循环的参数(需要循环什么)。我使用Java作为示例语言,但是许多语言都支持这种类型的函数式编程,它可以消除代码中的循环。最简单的情况是对列表中的每个元素执行操作。Listlist=List.of(1,2,3);//bareforloop.for(inti:list){System.out.println("int="+i);}//controlledforeachlist.forEach(i->System.out.println("int="+i));在这种最简单的情况下,两种方式都没有太大优势。但是第二种方法不能使用barefor循环,语法更简洁。我认为forEach语句也有问题,应该只用于副作用安全的方法。我所说的安全副作用是指不改变程序状态。上面的例子只是为了记录日志,所以使用是没有问题的。其他安全副作用的例子是写入文件、数据库或消息队列。不安全的副作用会改变程序状态。这是一个示例及其解决方案://badside-effect,theloopalterssumintsum=0;for(inti:list){sum+=i;}System.out.println("sum="+sum);//noside-effect,sumiscalculatedbyloopsum=list.stream().mapToInt(i->i).sum();System.out.println("sum="+sum);另一个常见的例子://badside-effect,theloopalterslist2Listlist2=newArrayList<>();for(inti:list){list2.add(i);}list2.forEach(i->System.out.println("int="+i));//没有副作用,secondlist是由thelooplist2构建的=list.stream().collect(Collectors.toList());list2.forEach(i->System.out.println("int="+i));当你需要在列表项方法中处理索引时会有问题,但是可以通过如下方式解决://bareforloopwithindex:for(inti=0;iSystem.out.println("itematindex"+i+"="+list.get(i)));老生常谈的问题:如何解决读取文件中的每一行直到文件读取完毕的问题?BufferedReaderreader=newBufferedReader(newInputStreamReader(LoopElimination.class.getResourceAsStream("/testfile.txt")));//whileloopwithclumsyllookingsyntaxStringline;while((line=reader.readLine())!=null){System.out.println(line);}reader=newBufferedReader(newInputStreamReader(LoopElimination.class.getResourceAsStream("/testfile.txt")));//lessclumsysyntaxreader.lines().forEach(l->System.out.println(l));有一个很简单的方法可以处理上面的情况lines方法可以返回Stream类型,但是如果一个字符一个字符一个字符读取呢?InputStream类没有返回Stream的方法。我们必须创建自己的流:InputStreamis=LoopElimination.class.getResourceAsStream("/testfile.txt");//whileloopwithclumsylookingsyntaxintc;while((c=is.read())!=-1){System.out.print((char)c);}//ButthisisevenuglierInputStreamnis=LoopElimination.class.getResourceAsStream("/testfile.txt");//异常处理使得函数式编程变丑了Stream.generate(()->{try{returnnis.read();}catch(IOExceptionex){thrownewRuntimeException("Errorreadingfromfile",ex);}}).takeWhile(ch->ch!=-1).forEach(ch->System.out.print((char)(int)ch));在这种情况下,while循环看起来更好。此外,Stream版本还使用了generate函数,可以返回无限的项目流,因此必须进一步检查以确保生成过程因takeWhile方法而终止。InputStream类是有问题的,因为它缺少用于创建可以轻松转换为Stream的迭代器的peek方法。它还会抛出一个已检查的异常,从而使函数式编程变得混乱。在这种情况下,可以使用while语句让PR通过。为了让上面的问题更简洁,可以新建一个IterableInputStream类型,如下:Character>(){publicbooleanhasNext(){try{//poorman'speek:is.mark(1);booleanret=is.read()!=-1;is.reset();returnret;}catch(IOExceptionex){thrownewRuntimeException("Errorreadinginputstream",ex);}}publicCharacternext(){try{return(char)is.read();}catch(IOExceptionex){thrownewRuntimeException("Errorreadinginputstream",ex);}}};}}这个大大简化了循环问题://useapredefinedinputstreamiterator:InputStreamIterableit=newInputStreamIterable(LoopElimination.class.getResourceAsStream("/testfile.txt"));StreamSupport.stream(it.spliterator(),false).forEach(ch->System.out.print(ch));如果你经常遇到这种while循环,那么你可以创建并使用一个特殊的Iterable类。但是如果只用一次,就不用费心了,这只是Java新旧不兼容的一个例子。所以,下次你在代码中写for语句或while语句时,停下来想想如何用forEach或Stream更好地完成你的代码。