笔记来源:
Scala谜题
Scala 特别强调要书写简单、简洁的代码。匿名函数的语法 arg => expr
,使它很容易用最小模板构建函数字面量,甚至函数由多个语句组成时也一样可以。
用有自解释参数的函数还可以做得更好,而且还可以用占位符语法。占位符语法可以省去参数声明:
List(1, 2).map { i => i + 1}
List(1, 2).map { _ + 1}
以上两个语句是等价的。但如果使用如下语句:
List(1,2).map { i => println("Hi");i + 1 }
List(1,2).map { println("Hi"); _ + 1 }
会发现,它们的输出结果不一样:
Hi
Hi
List[Int] = List(2,3)
Hi
List[Int] = List(2,3)
为什么使用占位符语法的函数与预计结构不一样呢?因为匿名函数常常被当作参数传递,在代码中往往会看到它们在花括号 { ... }
里,就很容易认为这些花括号表示一个匿名函数。但是,实际上它们只是界定了一个块表达式,一个或多个表达式最后决定了这个块的结果。
两个代码块的解析方式决定了它们有不同的行为。第一个语句 { i => println("Hi"); i + 1 }
被当成一个 arg => expr
形式的函数字面量表达式,这里的表达式是块 println("Hi"); i + 1
。
第二个表达式中,代码块被认为是 println("Hi")
和 _ + 1
两个表达式。当这个代码块执行的时候,将最后一个表达式(便利性所需的函数类型,Int => Int
)传递给map。其中的println语句不是函数体的一部分,它是在map的参数评估时被调用的,而不是作为map的一部分执行。
将刚刚那两个函数拆分后可以很方便看出它们的区别:
scala> val printAndAddOne = (i: Int) => { println("Hi"); i + 1 }
printAndAddOne: Int => Int = <function1>
scala> List(1, 2).map(printAndAddOne)
Hi
Hi
res29: List[Int] = List(2, 3)
scala> val printAndReturnAFunc = { println("Hi"); (_: Int) + 1 }
Hi
printAndReturnAFunc: Int => Int = <function1>
scala> List(1, 2).map(printAndReturnAFunc)
res30: List[Int] = List(2, 3)
Scala 鼓励简洁的代码,但太简洁时就会出现这样的情况。使用占位符语法时一定要注意由它所创建的函数范围。
这里学到的一点是:用占位符语法定义的匿名函数的范围只延伸到含有下划线 _
的表达式,而常规的匿名函数的函数体是包含从箭头标识符 =>
一直到代码块结束的所有代码。