=>, () => 및 Unit =>의 차이점은 무엇입니까?


153

인수를 취하지 않고 값을 반환하지 않는 함수를 나타내려고합니다 (알아야하는 경우 JavaScript에서 setTimeout 함수를 시뮬레이션하고 있습니다).

case class Scheduled(time : Int, callback :  => Unit)

"val"매개 변수가 이름별로 호출되지 않을 수 있습니다 "라고 컴파일하지 않습니다.

case class Scheduled(time : Int, callback :  () => Unit)  

컴파일하지만 대신 이상하게 호출해야합니다.

Scheduled(40, { println("x") } )

나는 이것을해야한다

Scheduled(40, { () => println("x") } )      

작동하는 것은

class Scheduled(time : Int, callback :  Unit => Unit)

그러나 똑같이 합리적인 방식으로 호출됩니다

 Scheduled(40, { x : Unit => println("x") } )

(Unit 유형의 변수는 무엇입니까?) 물론 원하는 것은 일반적인 함수 인 경우 호출하는 방식을 호출 할 수있는 생성자입니다.

 Scheduled(40, println("x") )

아기에게 병을 줘!


3
by-name parms와 함께 케이스 클래스를 사용하는 또 다른 방법은 예를 들어 보조 매개 변수 목록에 넣는 것입니다 case class Scheduled(time: Int)(callback: => Unit). 보조 매개 변수 목록이 공개적으로 노출되지 않았거나 생성 된 equals/ hashCode메소드에 포함되지 않았기 때문에 작동 합니다.
nilskp

By-name 매개 변수와 0-arity 함수의 차이점에 대한 몇 가지 흥미로운 측면 이이 질문 과 답변 에서 찾을 수 있습니다. 실제로이 질문을 찾았을 때 찾고 있던 것입니다.
lex82

답변:


234

이름 별 전화 : => 유형

=> Type표기법은 이름 별 호출을 나타내며 매개 변수를 전달할 수있는 많은 방법 중 하나입니다 . 익숙하지 않다면 요즘 위키 백과 기사를 읽어 보는 것이 좋습니다. 요즘은 대부분의 가치 별 기준이며 기준 별 기준입니다.

의미는 전달 된 것이 함수 내부의 값 이름으로 대체 된다는 것입니다 . 예를 들어 다음 기능을 사용하십시오.

def f(x: => Int) = x * x

이렇게 전화하면

var y = 0
f { y += 1; y }

그런 다음 코드는 다음과 같이 실행됩니다

{ y += 1; y } * { y += 1; y }

식별자 이름 충돌이 발생하면 어떤 일이 발생하는지 지적합니다. 기존의 이름 별 통화에서는 이름 충돌을 피하기 위해 캡처 방지 대체라는 메커니즘이 사용됩니다. 그러나 Scala에서는 동일한 결과로 다른 방식으로 구현됩니다. 매개 변수 내의 식별자 이름은 호출 된 함수의 식별자를 참조하거나 섀도 잉 할 수 없습니다.

다른 두 가지를 설명한 후에 언급 할 이름 별 통화와 관련된 몇 가지 다른 점이 있습니다.

0-arity 함수 : () => Type

구문 () => Type은의 유형을 나타냅니다 Function0. 즉, 매개 변수를 사용하지 않고 무언가를 반환하는 함수입니다. 이는 메서드를 호출하는 것과 같습니다 size(). 매개 변수를 사용하지 않고 숫자를 반환합니다.

그러나이 구문은 익명 함수 리터럴 의 구문과 매우 유사하다는 점이 흥미 롭습니다. 이는 혼동의 원인이됩니다. 예를 들어

() => println("I'm an anonymous function")

arity 0의 익명 함수 리터럴이며 유형

() => Unit

그래서 우리는 쓸 수 있습니다 :

val f: () => Unit = () => println("I'm an anonymous function")

그러나 유형과 값을 혼동하지 않는 것이 중요합니다.

단위 => 유형

이것은 실제로 단지 Function1첫 번째 매개 변수 유형 Unit입니다. 작성하는 다른 방법은 (Unit) => Type또는 Function1[Unit, Type]입니다. 문제는 ... 이것이 원하는 것이 아닐 것입니다. Unit유형의 주요 목적은 그래서 이해가되지 않습니다에 관심되지 않은 값을 표시한다 받을 그 값을.

예를 들어,

def f(x: Unit) = ...

무엇을 할 수 x있습니까? 단일 값만 가질 수 있으므로받을 필요가 없습니다. 가능한 함수 중 하나는 다음을 반환하는 체인 함수입니다 Unit.

val f = (x: Unit) => println("I'm f")
val g = (x: Unit) => println("I'm g")
val h = f andThen g

andThen정의되어 Function1있고 우리가 연결하는 함수가 반환되고 있기 때문에이를 연결 시킬 수 Unit있는 유형으로 정의해야 Function1[Unit, Unit]했습니다.

혼란의 근원

혼동의 첫 번째 원인은 0-arity 함수에 존재하는 유형과 리터럴의 유사성이 이름 별 호출에도 존재한다고 생각하는 것입니다. 다시 말해,

() => { println("Hi!") }

의 리터럴입니다 () => Unit.

{ println("Hi!") }

의 리터럴이됩니다 => Unit. 그렇지 않습니다. 그것은 리터럴이 아닌 코드 블록입니다 .

혼동의 또 다른 원인은 Unittype ()0-arity 매개 변수 목록처럼 보이지만 그렇지 않다는 것입니다.


2 년 후 첫 투표를해야 할 수도 있습니다. 누군가 크리스마스의 case => 구문에 대해 궁금해하고 있으며이 답변을 정식적이고 완전한 것으로 추천 할 수는 없습니다! 세상은 어떻게됩니까? 아마도 마야 사람들은 일주일 만에 쉬었을 것입니다. 그들은 윤년을 올바르게 파악 했습니까? 일광 절약?
som-snytt

@ som-snytt 글쎄, 질문은에 대해 묻지 case ... =>않았으므로 언급하지 않았습니다. 슬프지만 사실이야. :-)
Daniel C. Sobral

1
@Daniel C. Sobral은 "그것은 리터럴이 아니라 코드 블록"이라고 설명해 주시겠습니까? 부품. 그렇다면 둘 사이의 정확한 차이점은 무엇입니까?
nish1013

2
@ nish1013 "literal"은 값 ( 일부 예 에서는 정수 1, 문자 'a', 문자열 "abc"또는 함수 () => println("here"))입니다. 인수로 전달되거나 변수 등에 저장 될 수 있습니다. "코드 블록"은 문장의 구문 구분입니다. 값이 아니거나 전달할 수 없습니다.
Daniel C. Sobral

1
@Alex 이것은 (Unit) => Typevs 와 같은 차이점입니다 () => Type. 첫 번째는 a Function1[Unit, Type]이고 두 번째는 a Function0[Type]입니다.
Daniel C. Sobral

36
case class Scheduled(time : Int, callback :  => Unit)

case수정 암시한다 val생성자에 각 인수의 아웃. 따라서 (누군가 지적했듯이) 제거 case하면 이름 별 호출 매개 변수를 사용할 수 있습니다. 컴파일러는 어쨌든 허용 할 수 있지만 val callback로 변형하는 대신 생성하면 사람들을 놀라게 할 수 있습니다 lazy val callback.

callback: () => Unit지금 변경하면 이름 별 호출 매개 변수 대신 함수가 사용됩니다. 분명히 함수를 저장할 수 있으므로 val callback아무런 문제가 없습니다.

원하는 것을 얻는 가장 쉬운 방법은 ( Scheduled(40, println("x") )이름 별 호출 매개 변수를 사용하여 람다를 전달하는 경우) 아마도 건너 뛰고 처음에는 얻을 수없는 것을 case명시 적으로 만드는 것 apply입니다.

class Scheduled(val time: Int, val callback: () => Unit) {
    def doit = callback()
}

object Scheduled {
    def apply(time: Int, callback: => Unit) =
        new Scheduled(time, { () => callback })
}

사용:

scala> Scheduled(1234, println("x"))
res0: Scheduled = Scheduled@5eb10190

scala> Scheduled(1234, println("x")).doit
x

3
케이스 클래스를 유지하고 기본 적용을 무시하지 않는 이유는 무엇입니까? 또한 컴파일러는 고유 한 의미가 있고, 지연이 최대 한 번이고 이름이 모든 참조를 갖기 때문에 이름 별을 게으른 값으로 변환 할 수 없습니다.
Viktor Klang

@ViktorKlang 케이스 클래스의 기본 apply 메소드를 어떻게 재정의 할 수 있습니까? stackoverflow.com/questions/2660975/…
Sawyer

object ClassName {def apply (…) :… =…}
Viktor Klang

4 년 후 저는 선택한 답변이 실제로받은 질문이 아니라 제목에있는 질문에만 답변했음을 알고 있습니다.
Malvolio

1

이 질문에서는 JavaScript로 SetTimeOut 함수를 시뮬레이션하려고합니다. 이전 답변을 바탕으로 다음 코드를 작성합니다.

class Scheduled(time: Int, cb: => Unit) {
  private def runCb = cb
}

object Scheduled {
  def apply(time: Int, cb: => Unit) = {
    val instance = new Scheduled(time, cb)
    Thread.sleep(time*1000)
    instance.runCb
  }
}

REPL에서는 다음과 같은 것을 얻을 수 있습니다.

scala> Scheduled(10, println("a")); Scheduled(1, println("b"))
a
b

시뮬레이션은 함수를 차단하지만 SetTimeOut은 비 차단이기 때문에 시뮬레이션은 SetTimeOut과 정확히 동일하게 동작하지 않습니다.


0

나는 이런 식으로한다 (단지 적용하고 싶지 않다) :

case class Thing[A](..., lazy: () => A) {}
object Thing {
  def of[A](..., a: => A): Thing[A] = Thing(..., () => a)
}

전화 해

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