用递归术语来表达你的问题常常会使问题简化,如果应用了尾递归优化(可以通过注释检测),编译器甚至会将你的代码转换为正常的循环。对比一个标准的命令式版本的堆排序(fix-down):

    每次进入while循环,我们工作在前一次迭代时污染过的状态。每个变量的值是那一分支所进入函数,当找到正确的位置时会在循环中返回。
    (敏锐的读者会在Dijkstra的“Go To声明是有害的”一文找到相似的观点)

    考虑尾递归的实现:

    1. final def fixDown(heap: Array[T], i: Int, j: Int) {
    2. if (j < i*2) return
    3. val m = if (j == i*2 || heap(2*i) < heap(2*i+1)) 2*i else 2*i + 1
    4. if (heap(m) < heap(i)) {
    5. swap(heap, i, m)
    6. fixDown(heap, m, j)
    7. }

    每次迭代都是一个明确定义的历史清白的变量,并且没有引用单元:到处都是不变的(invariants)。更容易实现,也容易阅读。也没有性能方面的惩罚:因为方法是尾递归的,编译器会转换为标准的命令式的循环。

    Returns可以用于切断分支和建立不变量(establish invariants)。这减少了嵌套,并且容易推断后续的代码的正确性,从而帮助了读者。这尤其适用于卫语句(guard clauses):

    1. def compare(a: AnyRef, b: AnyRef): Int = {
    2. if (a eq b)
    3. val d = System.identityHashCode(a) compare System.identityHashCode(b)
    4. if (d != 0)
    5. return d
    6. // slow path..
    7. }

    使用return增加了可读性

    上面是针对命令式语言的,在Scala中鼓励省略return

    1. def suffix(i: Int) =
    2. else if (i == 2) "nd"
    3. else if (i == 3) "rd"

    但使用模式匹配更好:

    1. def suffix(i: Int) = i match {
    2. case 1 => "st"
    3. case 2 => "nd"
    4. case 3 => "rd"
    5. case _ => "th"
    6. }

    在字节码层实现为一个异常的捕获/声明(catching/throwing)对,用在频繁的执行的代码中,会有性能影响。

    for对循环和聚集提供了简洁和自然的表达。 它在扁平化(flattening)很多序列时特别有用。for语法通过分配和派发闭包隐藏了底层的机制。这会导致意外的开销和语义;例如:

    1. for (item <- container) {
    2. if (item != 2) return
    3. }

    如果容器延迟计算(delays computation)会引起运行时错误,使返回不在本地上下文 (making the return nonlocal)

    因为这些原因,常常更可取的是直接调用foreach, flatMap, map和filter —— 但在其意义清楚的时候使用for。

    1. assert(stream != null)

    相反,require用于表达API契约: