根据我的理解,在Scala中,函数也可以被调用
传递或 的名字
例如,给定以下声明,我们是否知道函数将如何被调用?
声明:
def f (x:Int, y:Int) = x;
Call
f (1,2)
f (23+55,5)
f (12+3, 44*11)
请问规则是什么?
根据我的理解,在Scala中,函数也可以被调用
传递或 的名字
例如,给定以下声明,我们是否知道函数将如何被调用?
声明:
def f (x:Int, y:Int) = x;
Call
f (1,2)
f (23+55,5)
f (12+3, 44*11)
请问规则是什么?
当前回答
正如我假设的那样,上面讨论的按值调用函数只是将值传递给函数。根据Martin Odersky的说法,这是Scala遵循的一种评估策略,在函数评估中扮演着重要的角色。但是,让叫名字变得简单。这就像将函数作为参数传递给方法(也称为高阶函数)。当方法访问传递参数的值时,它调用传递函数的实现。如下:
根据@dhg示例,首先创建方法如下:
def something() = {
println("calling something")
1 // return value
}
这个函数包含一个println语句并返回一个整数值。创建函数,其参数为call-by-name:
def callByName(x: => Int) = {
println("x1=" + x)
println("x2=" + x)
}
此函数参数定义了一个匿名函数,返回一个整数值。在这个x中包含一个函数的定义,它传递0个参数,但返回int值,我们的something函数包含相同的签名。调用函数时,将函数作为参数传递给callByName。但在按值调用的情况下,它只将整数值传递给函数。我们将函数调用如下:
scala> callByName(something())
calling something
x1=1
calling something
x2=1
在这里,我们的something方法调用了两次,因为当我们在callByName方法中访问x的值时,它调用了something方法的定义。
其他回答
我不认为这里所有的答案都是正确的:
在按值调用中,参数只计算一次:
def f(x : Int, y :Int) = x
// following the substitution model
f(12 + 3, 4 * 11)
f(15, 4194304)
15
你可以在上面看到,不管是否需要,所有的参数都会被求值,通常按值调用可以很快,但并不总是像这种情况。
如果评估策略是按名称调用的,那么分解将是:
f(12 + 3, 4 * 11)
12 + 3
15
正如你所看到的,我们从来不需要计算4 * 11,因此节省了一些计算,这有时可能是有益的。
正如我假设的那样,上面讨论的按值调用函数只是将值传递给函数。根据Martin Odersky的说法,这是Scala遵循的一种评估策略,在函数评估中扮演着重要的角色。但是,让叫名字变得简单。这就像将函数作为参数传递给方法(也称为高阶函数)。当方法访问传递参数的值时,它调用传递函数的实现。如下:
根据@dhg示例,首先创建方法如下:
def something() = {
println("calling something")
1 // return value
}
这个函数包含一个println语句并返回一个整数值。创建函数,其参数为call-by-name:
def callByName(x: => Int) = {
println("x1=" + x)
println("x2=" + x)
}
此函数参数定义了一个匿名函数,返回一个整数值。在这个x中包含一个函数的定义,它传递0个参数,但返回int值,我们的something函数包含相同的签名。调用函数时,将函数作为参数传递给callByName。但在按值调用的情况下,它只将整数值传递给函数。我们将函数调用如下:
scala> callByName(something())
calling something
x1=1
calling something
x2=1
在这里,我们的something方法调用了两次,因为当我们在callByName方法中访问x的值时,它调用了something方法的定义。
在按值调用中,表达式的值是在函数调用时预先计算的,并且该特定值作为参数传递给相应的函数。相同的值将在整个函数中使用。
而在名称调用中,表达式本身作为参数传递给函数,并且仅在调用特定参数时在函数内部计算。
Scala中按名称调用和按值调用之间的区别可以通过下面的例子更好地理解:
代码片段
object CallbyExample extends App {
// function definition of call by value
def CallbyValue(x: Long): Unit = {
println("The current system time via CBV: " + x);
println("The current system time via CBV " + x);
}
// function definition of call by name
def CallbyName(x: => Long): Unit = {
println("The current system time via CBN: " + x);
println("The current system time via CBN: " + x);
}
// function call
CallbyValue(System.nanoTime());
println("\n")
CallbyName(System.nanoTime());
}
输出
The current system time via CBV: 1153969332591521
The current system time via CBV 1153969332591521
The current system time via CBN: 1153969336749571
The current system time via CBN: 1153969336856589
在上面的代码片段中,对于函数调用CallbyValue(system . nanotime()),系统纳米时间是预先计算的,并且预先计算的值已将参数传递给函数调用。
但是在CallbyName(System.nanoTime())函数调用中,表达式“System.nanoTime())”本身作为参数传递给函数调用,当在函数内部使用该参数时,将计算该表达式的值。
注意CallbyName函数的函数定义,其中有一个=>符号分隔参数x及其数据类型。这里的特定符号表明该函数是按名称调用类型的。
换句话说,按值调用的函数实参在进入函数之前求值一次,而按名称调用的函数实参仅在需要时才在函数内部求值。
希望这能有所帮助!
通常,函数的参数是值形参;也就是说,参数的值是在传递给函数之前确定的。但是,如果我们需要编写一个函数,该函数接受一个表达式作为参数,而我们不希望在函数内调用该表达式时才计算该表达式,该怎么办?对于这种情况,Scala提供了名称调用参数。
名称调用机制将一个代码块传递给被调用方,每次被调用方访问参数时,都会执行该代码块并计算该值。
object Test {
def main(args: Array[String]) {
delayed(time());
}
def time() = {
println("Getting time in nano seconds")
System.nanoTime
}
def delayed( t: => Long ) = {
println("In delayed method")
println("Param: " + t)
t
}
}
1. C:/>scalac Test.scala 2. scala Test 3. In delayed method 4. Getting time in nano seconds 5. Param: 81303808765843 6. Getting time in nano seconds
按值调用是常见的用例,这里有很多答案。
名称调用将一个代码块传递给调用方,每次调用时 调用方访问参数,执行代码块,然后 值被计算。
我将尝试用下面的用例以更简单的方式演示按名调用
示例1:
function下面是按名称调用的简单示例/用例,它将function作为参数并给出所经过的时间。
/**
* Executes some code block and prints to stdout the
time taken to execute the block
for interactive testing and debugging.
*/
def time[T](f: => T): T = {
val start = System.nanoTime()
val ret = f
val end = System.nanoTime()
println(s"Time taken: ${(end - start) / 1000 / 1000} ms")
ret
}
示例2:
apache spark(与scala一起)使用按名称调用的方式使用日志记录,参见日志特性 其中其惰性计算是否日志。isInfoEnabled或否。
protected def logInfo(msg: => String) {
if (log.isInfoEnabled) log.info(msg)
}