Call by name vs call by value in Scala, clarificat

2019-01-01 03:01发布

As I understand it, in Scala, a function may be called either

  • by-value or
  • by-name

For example, given the following declarations, do we know how the function will be called?

Declaration:

def  f (x:Int, y:Int) = x;

Call

f (1,2)
f (23+55,5)
f (12+3, 44*11)

What are the rules please?

标签: scala
16条回答
余生无你
2楼-- · 2019-01-01 03:20

Here is an example from Martin Odersky:

def test (x:Int, y: Int)= x*x

We want to examine the evaluation strategy and determine which one is faster (less steps) in these conditions:

test (2,3)

call by value: test(2,3) -> 2*2 -> 4
call by name: test(2,3) -> 2*2 -> 4
Here the result is reached with the same number of steps.

test (3+4,8)

call by value: test (7,8) -> 7*7 -> 49
call by name: (3+4) (3+4) -> 7(3+4)-> 7*7 ->49
Here call by value is faster.

test (7,2*4)

call by value: test(7,8) -> 7*7 -> 49
call by name: 7 * 7 -> 49
Here call by name is faster

test (3+4, 2*4) 

call by value: test(7,2*4) -> test(7, 8) -> 7*7 -> 49
call by name: (3+4)(3+4) -> 7(3+4) -> 7*7 -> 49
The result is reached within the same steps.

查看更多
看淡一切
3楼-- · 2019-01-01 03:21

Parameters are usually pass by value, which means that they'll be evaluated before being substituted in the function body.

You can force a parameter to be call by name by using the double arrow when defining the function.

// first parameter will be call by value, second call by name, using `=>`
def returnOne(x: Int, y: => Int): Int = 1

// to demonstrate the benefits of call by name, create an infinite recursion
def loop(x: Int): Int = loop(x)

// will return one, since `loop(2)` is passed by name so no evaluated
returnOne(2, loop(2))

// will not terminate, since loop(2) will evaluate. 
returnOne(loop(2), 2) // -> returnOne(loop(2), 2) -> returnOne(loop(2), 2) -> ... 
查看更多
只靠听说
4楼-- · 2019-01-01 03:25

To iteratate @Ben's point in the above comments, I think it's best to think of "call-by-name" as just syntactic sugar. The parser just wraps the expressions in anonymous functions, so that they can be called at a later point, when they are used.

In effect, instead of defining

def callByName(x: => Int) = {
  println("x1=" + x)
  println("x2=" + x)
}

and running:

scala> callByName(something())
calling something
x1=1
calling something
x2=1

You could also write:

def callAlsoByName(x: () => Int) = {
  println("x1=" + x())
  println("x2=" + x())
}

And run it as follows for the same effect:

callAlsoByName(() => {something()})

calling something
x1=1
calling something
x2=1
查看更多
萌妹纸的霸气范
5楼-- · 2019-01-01 03:25

I will try to explain by a simple use case rather than by just providing an example

Imagine you want to build a "nagger app" that will Nag you every time since time last you got nagged.

Examine the following implementations:

object main  {

    def main(args: Array[String]) {

        def onTime(time: Long) {
            while(time != time) println("Time to Nag!")
            println("no nags for you!")
        }

        def onRealtime(time: => Long) {
            while(time != time) println("Realtime Nagging executed!")
        }

        onTime(System.nanoTime())
        onRealtime(System.nanoTime())
    }
}

In the above implementation the nagger will work only when passing by name the reason is that, when passing by value it will re-used and therefore the value will not be re-evaluated while when passing by name the value will be re-evaluated every time the variables is accessed

查看更多
骚的不知所云
6楼-- · 2019-01-01 03:27

See this:

    object NameVsVal extends App {

  def mul(x: Int, y: => Int) : Int = {
    println("mul")
    x * y
  }
  def add(x: Int, y: Int): Int = {
    println("add")
    x + y
  }
  println(mul(3, add(2, 1)))
}

y: => Int is call by name. What is passed as call by name is add(2, 1). This will be evaluated lazily. So output on console will be "mul" followed by "add", although add seems to be called first. Call by name acts as kind of passing a function pointer.
Now change from y: => Int to y: Int. Console will show "add" followed by "mul"! Usual way of evaluation.

查看更多
低头抚发
7楼-- · 2019-01-01 03:28

Typically, parameters to functions are by-value parameters; that is, the value of the parameter is determined before it is passed to the function. But what if we need to write a function that accepts as a parameter an expression that we don't want evaluated until it's called within our function? For this circumstance, Scala offers call-by-name parameters.

A call-by-name mechanism passes a code block to the callee and each time the callee accesses the parameter, the code block is executed and the value is calculated.

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
查看更多
登录 后发表回答