스칼라에서 이름으로 전화 및 값으로 전화, 설명 필요


239

내가 알기로, 스칼라에서 함수는

  • 값별 또는
  • 이름으로

예를 들어, 다음과 같은 선언에서 함수가 어떻게 호출되는지 알고 있습니까?

선언:

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

요구

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

규칙은 무엇입니까?

답변:


540

제시 한 예제는 값별 호출 만 사용하므로 차이점을 보여주는 새롭고 간단한 예제를 제공합니다.

먼저 부작용이있는 함수가 있다고 가정 해 봅시다. 이 함수는 무언가를 출력 한 다음를 반환합니다 Int.

def something() = {
  println("calling something")
  1 // return value
}

이제 우리는 Int하나의 값을 호출하는 스타일 ( x: Int)로, 다른 하나는 이름을 부르는 스타일 ( )로 인수를 취하는 것을 제외하고는 정확히 동일한 인수 를 받아들이는 두 함수를 정의 할 것입니다 x: => Int.

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

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

이제 부작용 기능으로 호출하면 어떻게됩니까?

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

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

따라서 값별 호출 버전에서는 전달 된 함수 호출 ( something()) 의 부작용 이 한 번만 발생 했음을 알 수 있습니다 . 그러나 이름 별 통화 버전에서는 부작용이 두 번 발생했습니다.

값별 호출 함수는 함수를 호출하기 전에 전달 된 표현식의 값을 계산하므로 매번 동일한 값에 액세스하기 때문입니다. 대신, 이름 별 호출 함수 는 전달 될 때마다 전달 된 표현식의 값을 재 계산 합니다.


296
나는 항상이 용어가 불필요하게 혼동된다고 생각했습니다. 함수는 이름 별 호출 대 값별 상태에 따라 여러 매개 변수를 가질 수 있습니다. 그것이없는 것을 그래서 함수 호출 별 이름 또는 통화별로 값, 그것의 각각의 것을 파라미터가 될 수있다 통과 그래밍 이름이나 통과하여 값. 또한 "이름 별 호출"은 names 와 관련이 없습니다 . => Int와 다른 유형 입니다 Int. Int"vs 인수를 생성하는 인수가없는 기능 " Int입니다. 일급 함수를 얻은 후에 는이를 설명하기 위해 이름 별 용어를 만들 필요 가 없습니다 .

2
@Ben, 그것은 몇 가지 질문에 답하는 데 도움이됩니다. 감사합니다. 더 많은 글들이 이름 별 통과의 의미를 명확하게 설명하기를 바랍니다.
Christopher Poile

3
@SelimOber 텍스트 f(2)가 type의 표현식으로 컴파일 Int되면 생성 된 코드는 fargument를 호출 2하고 결과는 표현식의 값입니다. 동일한 텍스트가 유형의 표현식으로 컴파일 => Int되면 생성 된 코드는 일종의 "코드 블록"에 대한 참조를 표현식의 값으로 사용합니다. 어느 쪽이든, 해당 유형의 값은 해당 유형의 매개 변수를 예상하는 함수에 전달 될 수 있습니다. 나는 매개 변수가 보이지 않고 변수 할당 으로이 작업을 수행 할 수 있다고 확신합니다. 이름이나 부름은 무엇과 관련이 있습니까?
Ben

4
@Ben 따라서 => Int"Int를 생성하는 인수가없는 함수"라면 어떻게 다른가 () => Int? 스칼라는 이것들을 다르게 취급하는 것 같습니다. 예를 들어 매개 변수의 유형으로 만 => Int유형으로 작동하지 않는 것 같습니다 val.
Tim Goodman

5
@TimGoodman 당신이 맞아요, 내가 만든 것보다 조금 더 복잡합니다. => Int편리함이며 함수 객체처럼 정확하게 구현되지는 않습니다 (아마도 유형 변수를 가질 수없는 => Int이유는 없지만 이것이 작동하지 않는 근본적인 이유는 없습니다). () => Int명시 적Int 으로 호출해야하며 함수로 전달 될 수있는을 반환하는 인수가없는 함수입니다. => Int일종의 "프록시 Int"이며, 이것으로 할 수있는 유일한 것은 (내재적으로) 호출하여을 얻는 것 Int입니다.
Ben

51

다음은 Martin Odersky의 예입니다.

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

다음과 같은 조건에서 평가 전략을 검토하고 어느 단계가 더 빠른지 (더 적은 단계) 결정합니다.

test (2,3)

값으로 호출 : test (2,3)-> 2 * 2-> 4
이름으로 호출 : test (2,3)-> 2 * 2-> 4
여기서 동일한 단계 수로 결과에 도달합니다.

test (3+4,8)

값으로 전화 : test (7,8)-> 7 * 7-> 49
이름으로 전화 : (3 + 4) (3 + 4)-> 7 (3 + 4)-> 7 * 7-> 49
여기에 전화 값이 빠릅니다.

test (7,2*4)

값으로 전화 : test (7,8)-> 7 * 7-> 49
이름으로 전화 : 7 * 7-> 49
여기에서 이름으로 전화하는 것이 더 빠릅니다

test (3+4, 2*4) 

값으로 호출 : test (7,2 * 4)-> test (7, 8)-> 7 * 7-> 49
이름으로 호출 : (3 + 4) (3 + 4)-> 7 (3 + 4) -> 7 * 7-> 49
같은 단계에서 결과에 도달합니다.


1
CBV의 세 번째 예에서는 test (7,14) 대신 test (7,8)를 의미한다고 생각합니다.
talonx

1
예는 스칼라 프로그래밍의 원칙 인 Coursera에서 발췌 한 것입니다. 강의 1.2. 이름 별 호출 def test (x:Int, y: => Int) = x * x은 매개 변수 y가 사용되지 않는다는 점에 유의해야합니다.
jerry 제리

1
좋은 예입니다! Coursera MOOC에서 가져온 :)
alxsimo 7

이것은 그 차이점에 대한 좋은 설명이지만, 질문 할 내용, 즉 두 가지 중 스칼라 전화 중 어떤 것에
대해서도 다루지 않습니다

16

예제의 경우 모든 매개 변수는 value 로만 정의 하기 때문에 함수에서 호출 되기 전에 평가 됩니다 . 이름으로 매개 변수를 정의 하려면 코드 블록을 전달해야합니다.

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

이런 식으로 매개 변수 x는 함수에서 호출 될 때까지 평가되지 않습니다 .

여기 의이 작은 포스트 는 이것도 잘 설명해줍니다.


10

위의 의견에서 @Ben의 요점을 반복하려면 "이름 별 전화"를 구문 설탕으로 생각하는 것이 가장 좋습니다. 파서는 표현식을 익명 함수로 래핑하여 나중에 사용될 때 호출 할 수 있도록합니다.

사실상, 정의하는 대신

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

그리고 달리기 :

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

당신은 또한 쓸 수 있습니다 :

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

같은 효과를 얻으려면 다음과 같이 실행하십시오.

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

calling something
x1=1
calling something
x2=1

<!-언어 : lang-scala-> def callAlsoByName (x : () => Int) = {println ( "x1 ="+ x ()) println ( "x2 ="+ x ( ))} 다음 : <!-language : lang-js-> callAlsoByName (() => something ())이 마지막 호출에서 something () 주위에 중괄호가 필요하지 않다고 생각합니다. 참고 : 귀하의 답변을 편집하려고 시도했지만 리뷰 작성자는 의견이나 별도의 답변이어야한다고 편집자가 거부했습니다.
lambdista

분명히 주석에서 구문 강조 를 사용할 수 없으므로 "<!-language : lang-scala->"부분을 무시하십시오! 내 의견을 편집했지만 5 분 안에 만 할 수 있습니다! :)
lambdista

1
나는 최근에도 이것에 부딪쳤다. 이 개념이 있지만, 사이 스칼라 차별화처럼 생각하는 괜찮 => T() => T. 첫 번째 유형을 매개 변수로 사용하는 함수는 두 번째 유형을 받아들이지 않습니다. scala는 @ScalaSignature컴파일 타임 오류를 발생시키기에 충분한 정보를 주석에 저장 합니다. 모두를위한 바이트 코드 => T() => T동일하지만이며입니다 Function0. 자세한 내용은 이 질문 을 참조하십시오.
vsnyc

6

예제를 제공하는 것이 아니라 간단한 사용 사례로 설명하려고합니다.

시간이 지날수록 매번 잔소리 되는 "비틀 거림 앱" 을 만들고 싶다고 상상해보십시오 .

다음 구현을 조사하십시오.

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())
    }
}

위의 구현에서 Nagger는 이름으로 전달할 때만 작동합니다. 이유는 값으로 전달할 때 다시 사용되므로 이름으로 전달할 때마다 값이 다시 평가되지 않고 값이 다시 평가되지 않기 때문입니다. 변수에 액세스 한 시간


4

일반적으로 함수에 대한 매개 변수는 값별 매개 변수입니다. 즉, 매개 변수 값은 함수에 전달되기 전에 결정됩니다. 그러나 함수 내에서 호출 될 때까지 평가하고 싶지 않은 표현식을 매개 변수로 받아들이는 함수를 작성해야하는 경우 어떻게해야합니까? 이러한 상황에서 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. 스칼라 테스트
 3. 지연된 방법
 4. 나노초로 시간 얻기
 5. 매개 변수 : 81303808765843
 6. 나노초로 시간 얻기

2

내가 가정 call-by-value한 것처럼 위에서 설명한 함수는 값을 함수에 전달합니다. 에 따르면 Martin Odersky이 기능 평가에서 중요한 역할을 스칼라에 의한 평가 전략의 후속이다. 그러나 간단하게하십시오 call-by-name. 그것과 마찬가지로 함수를 메소드의 인수로 전달하는 것으로도 알려져 Higher-Order-Functions있습니다. 메소드가 전달 된 매개 변수의 값에 액세스 할 때 전달 된 함수의 구현을 호출합니다. 아래:

@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. 그러나 call-by-value그것 의 경우 정수 값을 함수에 전달합니다. 다음과 같이 함수를 호출합니다.

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

이 우리에서 something우리의 가치를 액세스 할 때 때문에, 두 번 호출 방법 xcallByName방법의 (고화질)과의 전화,something 방법.


2

가치에 의한 호출은 여기에 많은 답변으로 설명 된 일반적인 사용 사례입니다.

Call-by-name 은 코드 블록을 호출자에게 전달하고 호출자가 매개 변수에 액세스 할 때마다 코드 블록이 실행되고 값이 계산됩니다.

아래의 사용 사례를 사용하여 이름별로 더 간단한 방법으로 전화를 시연하려고합니다.

예 1 :

이름별로 호출하는 간단한 예제 / 사용 사례는 함수 아래에 있으며, 함수를 매개 변수로 사용하고 시간이 경과합니다.

 /**
   * 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 :

아파치 스파크 (스칼라 포함)는 이름별로 호출을 사용하여 로깅을 사용Logging 합니다 . 아래 방법에서 지연 여부를 느리게 평가 하는 특성참조하십시오log.isInfoEnabled .

protected def logInfo(msg: => String) {
     if (log.isInfoEnabled) log.info(msg)
 }

2

A의 값에 의해 호출 상기 식의 값은 함수 호출시에 미리 계산하고 특정 값이 해당 함수에 파라미터로서 전달된다. 기능 전체에서 동일한 값이 사용됩니다.

반면 A의 이름으로 호출 표현식 자체 기능을 매개 변수로 전달되고, 이는 단지 특정 파라미터가 호출 될 때마다 기능, 내부 계산된다.

Scala에서 Call by Name과 Call by Value의 차이점은 아래 예제를 통해 더 잘 이해할 수 있습니다.

코드 스 니펫

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 ())"자체는 함수 호출에 매개 변수로 전달되며 해당 매개 변수가 함수 내부에서 사용될 때 해당 표현식의 값이 계산됩니다. .

매개 변수 x 와 해당 데이터 유형을 구분 하는 => 기호 가있는 CallbyName 함수의 함수 정의를 확인하십시오 . 이 특정 기호는 함수가 이름 유형별로 호출되었음을 나타냅니다.

다시 말해, 함수 별 호출 함수 인수는 함수를 입력하기 전에 한 번 평가되지만, 이름 별 함수 인수 호출은 필요할 때만 함수 내에서 평가됩니다.

도움이 되었기를 바랍니다!


2

다음은 현재 스칼라 코스를 수강하는 동료를 돕기 위해 작성한 간단한 예입니다. 내가 흥미로 웠던 것은 Martin이 강의에서 제시 한 && 질문 답변을 예로 사용하지 않았다는 것입니다. 어쨌든 이것이 도움이되기를 바랍니다.

val start = Instant.now().toEpochMilli

val calc = (x: Boolean) => {
    Thread.sleep(3000)
    x
}


def callByValue(x: Boolean, y: Boolean): Boolean = {
    if (!x) x else y
}

def callByName(x: Boolean, y: => Boolean): Boolean = {
    if (!x) x else y
}

new Thread(() => {
    println("========================")
    println("Call by Value " + callByValue(false, calc(true)))
    println("Time " + (Instant.now().toEpochMilli - start) + "ms")
    println("========================")
}).start()


new Thread(() => {
    println("========================")
    println("Call by Name " + callByName(false, calc(true)))
    println("Time " + (Instant.now().toEpochMilli - start) + "ms")
    println("========================")
}).start()


Thread.sleep(5000)

코드 출력은 다음과 같습니다.

========================
Call by Name false
Time 64ms
========================
Call by Value false
Time 3068ms
========================

1

매개 변수는 일반적으로 값으로 전달되므로 함수 본문에서 대체되기 전에 평가됩니다.

함수를 정의 할 때 이중 화살표를 사용하여 이름으로 매개 변수를 강제로 호출 할 수 있습니다.

// 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) -> ... 

1

인터넷에는이 질문에 대한 환상적인 답변이 이미 많이 있습니다. 누군가 주제가 도움이 될 수있는 경우를 대비하여 주제에 대해 수집 한 여러 가지 설명과 예제를 편집하여 작성합니다.

소개

가치 별 통화 (CBV)

일반적으로 함수에 대한 매개 변수는 값별 호출 매개 변수입니다. 즉, 매개 변수는 왼쪽에서 오른쪽으로 평가되어 함수 자체가 평가되기 전에 값을 결정합니다.

def first(a: Int, b: Int): Int = a
first(3 + 4, 5 + 6) // will be reduced to first(7, 5 + 6), then first(7, 11), and then 7

CBN (Call-by-Name)

그러나 함수 내에서 호출 될 때까지 평가하지 않는 식을 매개 변수로 받아들이는 함수를 작성해야하는 경우 어떻게해야합니까? 이러한 상황에서 Scala는 이름 별 호출 매개 변수를 제공합니다. 매개 변수가 그대로 함수에 전달되고 대체 후 평가가 수행됨을 의미

def first1(a: Int, b: => Int): Int = a
first1(3 + 4, 5 + 6) // will be reduced to (3 + 4) and then to 7

이름 별 호출 메커니즘은 코드 블록을 호출에 전달하고 호출이 매개 변수에 액세스 할 때마다 코드 블록이 실행되고 값이 계산됩니다. 다음 예제에서 delayed는 메소드가 입력되었음을 나타내는 메시지를 인쇄합니다. 다음으로 delayed는 값을 가진 메시지를 인쇄합니다. 마지막으로 지연은 't'를 반환합니다.

 object Demo {
       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)
       }
    }

지연된 방법
으로 나노초로 시간 얻기
매개 변수 : 2027245119786400

각 사례의 장단점

CBN : + 더 자주 종료 * 위의 종료 확인 * * 함수 본문의 평가에서 해당 매개 변수를 사용하지 않으면 함수 인수가 평가되지 않는다는 이점이 있습니다. 로드 시간이 길어지고 더 많은 메모리를 소비합니다.

CBV : + CBN보다 기하 급수적으로 더 효율적입니다. 이름으로 호출되는 인수 표현식의 반복 계산을 피하기 때문입니다. 모든 함수 인수를 한 번만 평가 + 명령이 평가 될 때 훨씬 더 잘 아는 경향이 있기 때문에 명령 효과와 부작용으로 훨씬 잘 재생됩니다. -파라미터 평가시 루프가 발생할 수 있습니다.

종료가 보장되지 않으면 어떻게됩니까?

식 e의 CBV 평가가 종료되면 e의 CBN 평가도 종료됩니다.-다른 방향은 사실이 아닙니다.

비 종료 예

def first(x:Int, y:Int)=x

먼저 표현식을 고려하십시오 (1, loop)

CBN : first (1, loop) → 1 CBV : first (1, loop) →이 표현식의 인수를 줄입니다. 하나는 루프이기 때문에 인수를 무한정 줄입니다. 종료되지 않습니다

각각의 사례 행동의 차이점

방법 테스트를 정의하자

Def test(x:Int, y:Int) = x * x  //for call-by-value
Def test(x: => Int, y: => Int) = x * x  //for call-by-name

사례 1 테스트 (2,3)

test(2,3)2*24

이미 평가 된 인수로 시작하기 때문에 값별 호출 및 이름 별 호출의 단계는 같습니다.

사례 2 테스트 (3 + 4,8)

call-by-value: test(3+4,8) → test(7,8)7 * 749
call-by-name: (3+4)*(3+4)7 * (3+4)7 * 749

이 경우 값별 호출은 더 적은 단계를 수행합니다.

Case3 테스트 (7, 2 * 4)

call-by-value: test(7, 2*4) → test(7,8)7 * 749
call-by-name: (7)*(7)49

우리는 두 번째 인수의 불필요한 계산을 피합니다

Case4 테스트 (3 + 4, 2 * 4)

call-by-value: test(7, 2*4) → test(7,8)7 * 749
call-by-name: (3+4)*(3+4)7*(3+4)7*749

다른 접근법

먼저 부작용이있는 함수가 있다고 가정 해 봅시다. 이 함수는 무언가를 출력 한 다음 Int를 반환합니다.

def something() = {
  println("calling something")
  1 // return value
}

이제 우리는 하나의 값으로 호출하는 스타일 (x : Int)에서 다른 하나는 이름으로 호출하는 스타일 (x : => Int).

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

이제 부작용 기능으로 호출하면 어떻게됩니까?

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

따라서 값별 호출 버전에서는 전달 된 함수 호출 (something ())의 부작용이 한 번만 발생했음을 알 수 있습니다. 그러나 이름 별 통화 버전에서는 부작용이 두 번 발생했습니다.

값별 호출 함수는 함수를 호출하기 전에 전달 된 표현식의 값을 계산하므로 매번 동일한 값에 액세스하기 때문입니다. 그러나 이름 별 호출 함수는 전달 될 때마다 전달 된 표현식의 값을 재 계산합니다.

이름 별 통화를 사용하는 것이 더 나은 예

보낸 사람 : https://stackoverflow.com/a/19036068/1773841

간단한 성능 예 : 로깅.

다음과 같은 인터페이스를 상상해 봅시다.

trait Logger {
  def info(msg: => String)
  def warn(msg: => String)
  def error(msg: => String)
}

그런 다음 다음과 같이 사용하십시오.

logger.info("Time spent on X: " + computeTimeSpent)

info 메소드가 아무것도 수행하지 않으면 (예를 들어, 로깅 레벨이 그보다 높게 구성 되었기 때문에), computeTimeSpent가 호출되지 않으므로 시간이 절약됩니다. 이것은 로거에서 많이 발생하며, 로그 작업에 비해 문자열 조작이 종종 비싸다.

정확성 예 : 논리 연산자.

아마도 다음과 같은 코드를 보았을 것입니다.

if (ref != null && ref.isSomething)

다음과 같이 && 메소드를 선언한다고 상상해보십시오.

trait Boolean {
  def &&(other: Boolean): Boolean
}

그런 다음 ref가 null 일 때 &&에 전달되기 전에 null 참조에서 isSomething이 호출되므로 오류가 발생합니다. 이러한 이유로 실제 선언은 다음과 같습니다.

trait Boolean {
  def &&(other: => Boolean): Boolean =
    if (this) this else other
}

1

예제를 살펴보면 차이점을 더 잘 이해하는 데 도움이됩니다.

현재 시간을 반환하는 간단한 함수를 정의합시다 :

def getTime = System.currentTimeMillis

이제 name 으로 함수 를 1 초 지연된 두 번 인쇄 하는 함수를 정의합니다 .

def getTimeByName(f: => Long) = { println(f); Thread.sleep(1000); println(f)}

그리고에 의해 하나의 :

def getTimeByValue(f: Long) = { println(f); Thread.sleep(1000); println(f)}

이제 각각을 호출하자 :

getTimeByName(getTime)
// prints:
// 1514451008323
// 1514451009325

getTimeByValue(getTime)
// prints:
// 1514451024846
// 1514451024846

결과는 차이점을 설명해야합니다. 스 니펫은 여기에서 사용할 수 있습니다 .


0

CallByName사용될 때 callByValue호출되고 명령문이 발생할 때마다 호출됩니다.

예를 들면 다음과 같습니다.

무한 루프가 있습니다. 예를 들어이 함수를 실행하면 scala프롬프트 가 표시되지 않습니다 .

scala> def loop(x:Int) :Int = loop(x-1)
loop: (x: Int)Int

callByName기능 이상 소요 loop인수로 방법 및 그것의 본체 내에서 사용되지 않는다.

scala> def callByName(x:Int,y: => Int)=x
callByName: (x: Int, y: => Int)Int

callByName메소드 실행시 scala함수 내부에서 루프 함수를 사용하는 곳이 없으므로 아무런 문제가 없습니다 ( 프롬프트가 나타납니다) callByName.

scala> callByName(1,loop(10))
res1: Int = 1
scala> 

callByValue기능 이상 소요 loop하여이 외부 함수를 실행하기 전에 평가 기능 또는 발현 내측 결과 파라미터로 방법 loop함수 재귀 실행 우리는 결코 scala프롬프트 백.

scala> def callByValue(x:Int,y:Int) = x
callByValue: (x: Int, y: Int)Int

scala> callByValue(1,loop(1))

0

이것 좀 봐:

    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는 이름으로 호출됩니다. 이름으로 전화로 전달되는 것은 add (2, 1)입니다. 이것은 게으르게 평가 될 것입니다. 따라서 add가 먼저 호출되는 것처럼 콘솔의 출력은 "mul"다음에 "add"가됩니다. 이름 별 호출은 함수 포인터를 전달하는 일종의 역할을합니다.
이제 y : => Int에서 y : Int로 변경하십시오. 콘솔에 "add"와 "mul"이 표시됩니다! 일반적인 평가 방법.


-2

나는 여기에있는 모든 대답이 올바른 정당화를한다고 생각하지 않습니다.

값별로 호출하면 인수는 한 번만 계산됩니다.

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

// following the substitution model

f(12 + 3, 4 * 11)
f(15, 4194304)
15

위의 모든 인수가 필요한지 여부에 따라 평가되며 일반적 call-by-value으로 빠를 수 있지만 항상이 경우와 같은 것은 아닙니다.

평가 전략 call-by-name이었다면 분해 는 다음과 같습니다.

f(12 + 3, 4 * 11)
12 + 3
15

위에서 볼 수 있듯이 우리는 평가할 필요가 없었기 4 * 11때문에 때때로 도움이 될 수있는 약간의 계산을 저장했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.