我是斯卡拉的新手。高阶函数跟随括号或括号块之间是否有任何差异?
例如:
List(1, 2, 3).map(i=> i + 1)
List(1, 2, 3).map {i => i + 1}
他们都得到相同的结果:List(2, 3, 4)
但是对于此示例List(1, 2).map { println("Hi"); _ + 1 }
结果如下,为什么 '嗨' 只打印一次?
Hi
List[Int] = List(2, 3)
答案 0 :(得分:8)
Scala中的一个块只是一个表达式。与括号一样,它们对于将代码组合在一起非常有用。与括号不同,块不包含一个表达式,但可以包含一个或多个表达式。块的值是其中最后一个表达式的值。
{ println("Hi"); _ + 1 }
相当于{ println("Hi"); (i: Int) => i + 1 }
。这是一个打印出"Hi"
的块,它的值是一个添加一个的函数。在执行退出块之前打印字符串,并且它生成的函数对println
没有任何了解。
以下是这些规则的一些示例:
list.map(i => i + 1)
// ^----------^
// function literal passed as argument
list.map(_ + 1)
// ^---^
// Underscore shorthand for above
list.map({ i => i + 1 })
// Identical to above.
// The block only contains one expression, so it has the value of that expression
// Otherwise stated: { expr } === expr
list.map({ println("Hi"); _ + 1 })
// ^-----2-----^ ^-3-^
// ^------------1---------^
// 1: The argument to map is the value of this block
// 2: The first statement of the block prints something. This is only executed once,
// because it's not the *block* being passed as argument, it's its value.
// 3: Function literal in underscore notation. This is the value of the block
// and this is what map sees.
// Order of operations (approx. bytecode):
// load list onto stack
// load string "Hi" onto stack
// call println and pop string off stack
// create function (i => i + 1) on top of stack
// invoke map with argument (i => i + 1), popping list and function off stack
list.map { println("Hi"); _ + 1 }
// Identical to above, but Scala lets you omit the () because you are using {}
list.map({ i => println("Hi"); i + 1 })
// Function literals grow as big as they can be.
// The block contains only one expression, which is (i => println("Hi"); i + 1)
// This function prints "Hi" and then returns i + 1
// This call to map will print "Hi" for every element
list.map { i => println("Hi"); i + 1 }
// Identical to above, but Scala lets you omit the () because you are using {}
此外,还有一些名称参数需要处理。按名称参数声明如下:
def func(a: => String) // Not () => String, it's => String
当你有名字参数时,那么
func { println("x"); _ + 1 }
实际上将整个块作为参数传递。该块仍然评估为i => i + 1
,但{<1}}在评估发生时控制。具体来说,块的代码变为func
并传递到Function0
,可以根据需要多次调用它,带有副作用。这可以用来产生很好的效果,基本上允许普通函数像自定义控制流操作符一样工作:
func
注意如何在块周围省略括号,这实际上使得它看起来像定义控制流操作符的能力。
答案 1 :(得分:1)
通常,您可以使用括号括起简单的函数参数:
l.map( x => x * 2 )
用大括号括起更复杂的代码块或部分函数,包括case
模式匹配:
l.map{ x =>
val y = x * 2
y
}
l.map{
case x if x%2 == 0 => x * 2
case _ => 0
}
至于Hi
仅打印一次的原因,List(1, 2).map{ println("Hi"); _ + 1 }
与List(1, 2).map{ println("Hi"); x => x + 1 }
没有区别。要在println
次迭代中包含map
:
List(1, 2).map{ x => println("Hi"); x + 1 }
// Hi
// Hi
// res1: List[Int] = List(2, 3)