由 Java 到 Scala:如何优雅的跳出循环

在开发过程中,我们经常会遇到这样的需求:循环执行某个操作,当满足一定条件的时候循环终止。最常见的场景就是累加数组中的元素,一直到大于某个值,用伪代码来描述就是:

DO LOOP{ DO SOME THING IF SOME CONDITION BREAK}

如果我们使用 Java 来完成这样的需求,我们会这样完成我们的代码:

// List[1,2,3,4,5,6]int sum = 0;for(int i = 0; i < list.size(); i ++){ sum += list.get(i); if(sum > 4){ break; }}

在 Java 中,我们用一个break语句,就完成的从循环中跳出的工作。但在 Scala 中我们应该怎么做呢?要知道 Scala 特地没有在内置控制结构中包含 break 和 continue 是因为这两个控制结构和函数式编程有点格格不入。那么下面我将介绍几种在 Scala 中跳出循环的方法。

使用Return语句

没有break语句,那么作为 Java 的开发人员,第一时间就会想到return,还好 Scala 支持return

// List[1,2,3,4,5,6]var sum = 0list.foreach(i =>{ sum += i if(sum > 4){ return }})

使用Breaks

在 Scala 2.8以上版本中,Scala 增加了scala.util.control.Breaks包,通过导入这个包,你可以在 Scala 中写出和 Java 中相似的带break语句的循环。

import scala.util.control.Breaks._var sum = 0breakable { for (i <- 0 to 6) { sum += i if (sum >= 4) break }}

但是,这并不代表 Scala 从 2.8 版本开始支持break语句,它的实现实际是通过抛出异常给上级调用函数来达到控制循环的目的。Breaks的关键代码如下:

def breakable(op: => Unit) { try { op } catch { case ex: BreakControl => if (ex ne breakException) throw ex }}

所以,使用Breaks就等价于下面的代码:

object AllDone extends Exception { }var sum = 0try { for (i <- 0 to 6) { sum += i; if (sum>=4) throw AllDone }} catch { case AllDone =>}

一些优雅的方法

上面的方法虽然可以达到我们的目的,但和优雅还是差点距离,下面就回到我们的主题:如何优雅的跳出循环。

使用 Stream

Stream 是个很有意思的结构,它和列表相似,只不过它会延迟计算下一个元素,仅当需要的时候才会去计算。运用 Stream 的这个特性,我们可以用一种优雅的方式达到我们跳出循环的目的

var sum = 0(0 to 6).toStream.takeWhile(_ => sum < 4).foreach(i => sum+=i)

你可能会觉得这个程序有 Bug,因为咋一看takeWhile中并没有进行累加,只比较了sum < 4,而累加是在foreach中做的,takeWhile的条件应该永远为true,导致最后的结果是错误的。那么到底会不会这样呢?答案是:不会。因为 Stream 是 Lazy 的,它会延迟计算下一个元素,在这个例子中,takeWhile(_ => sum < 4)只会在每次foreach需要取 Stream 中的一个元素出来累加的时候才会执行一次,这就保证了判断条件的有效性。大致的执行序列如下

// List[1,2,3,4,5,6]var sum = 0takeWhile(_ => 0 < 4)foreach(1 => 0+=1)var sum = 1takeWhile(_ => 1 < 4)foreach(2 => 1+=2)....

使用递归代替循环

还有一种方法就是使用递归代替循环

var sum = 0def addTo(i: Int, max: Int) { sum += i; if (sum < max) addTo(i+1,max)}addTo(0,6)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Golang语言社区

【Go 语言社区】Go学习笔记:json处理

Encode 将一个对象编码成JSON数据,接受一个interface{}对象,返回[]byte和error: func Marshal(v interfac...

64612
来自专栏蘑菇先生的技术笔记

clojure基础入门(一)

1483
来自专栏desperate633

LintCode 单词切分题目分析

给出一个字符串s和一个词典,判断字符串s是否可以被空格切分成一个或多个出现在字典中的单词。

912
来自专栏Golang语言社区

在Go语言中使用JSON

Encode 将一个对象编码成JSON数据,接受一个interface{}对象,返回[]byte和error: func Marshal(v interfac...

3459
来自专栏iOS122-移动混合开发研究院

【读书笔记】A Swift Tour

素材:A Swift Tour 推荐下载Playground:Download Playground objc 自己较为熟悉,想熟悉下风头正劲的 swift。就...

3588
来自专栏极乐技术社区

使用ES6新特性开发微信小程序(1)

ECMAScript 6(简称ES6)是JavaScript语言的最新标准。因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015。 ...

2155
来自专栏Ldpe2G的个人博客

Scala typeclass 设计模式

本文的写作的灵感主要是看了这个视频 : Tutorial: Typeclasses in Scala with Dan Rosen

1556
来自专栏ml

CF---(452)A. Eevee

A. Eevee time limit per test 1 second memory limit per test 256 megabytes in...

43311
来自专栏对角另一面

lodash源码分析之获取数据类型

所有的悲伤,总会留下一丝欢乐的线索,所有的遗憾,总会留下一处完美的角落,我在冰峰的深海,寻找希望的缺口,却在惊醒时,瞥见绝美的阳光! ——几米 本文为读...

2773
来自专栏Ryan Miao

Java String.split()用法小结

在java.lang包中有String.split()方法,返回是一个数组 我在应用中用到一些,给大家总结一下,仅供大家参考: 1、如果用“.”作为分隔的话,必...

33811

扫码关注云+社区

领取腾讯云代金券