Scala에서 커링하는 두 가지 방법; 각각의 사용 사례는 무엇입니까?


83

내가 유지하는 Scala Style Guide의 Multiple Parameter Lists 에 대해 논의 하고 있습니다. 커링 에는 두 가지 방법이 있다는 것을 깨닫게 되었고 사용 사례가 무엇인지 궁금합니다.

def add(a:Int)(b:Int) = {a + b}
// Works
add(5)(6)
// Doesn't compile
val f = add(5)
// Works
val f = add(5)_
f(10) // yields 15

def add2(a:Int) = { b:Int => a + b }
// Works
add2(5)(6)
// Also works
val f = add2(5)
f(10) // Yields 15
// Doesn't compile
val f = add2(5)_

스타일 가이드는 그것들이 분명히 같지 않은데도 동일하다고 잘못 암시합니다. 가이드는 생성 된 카레 기능에 대해 지적하려고 노력하고 있으며, 두 번째 형태는 "책별"카레 링이 아니지만 여전히 첫 번째 형태와 매우 유사합니다. _)

이러한 양식을 사용하는 사람들로부터 한 양식을 다른 양식보다 사용할 때에 대한 합의는 무엇입니까?

답변:


137

다중 매개 변수 목록 방법

유형 추론

여러 매개 변수 섹션이있는 메서드는 첫 번째 섹션의 매개 변수를 사용하여 후속 섹션의 인수에 대해 예상되는 형식을 제공 할 형식 인수를 추론함으로써 로컬 형식 유추를 지원하는 데 사용할 수 있습니다. foldLeft표준 라이브러리에 이것의 표준적인 예가 있습니다.

def foldLeft[B](z: B)(op: (B, A) => B): B

List("").foldLeft(0)(_ + _.length)

이것이 다음과 같이 쓰여졌다면 :

def foldLeft[B](z: B, op: (B, A) => B): B

보다 명시적인 유형을 제공해야합니다.

List("").foldLeft(0, (b: Int, a: String) => a + b.length)
List("").foldLeft[Int](0, _ + _.length)

유창한 API

다중 매개 변수 섹션 메소드의 또 다른 용도는 언어 구조처럼 보이는 API를 만드는 것입니다. 호출자는 괄호 대신 중괄호를 사용할 수 있습니다.

def loop[A](n: Int)(body: => A): Unit = (0 until n) foreach (n => body)

loop(2) {
   println("hello!")
}

M 매개 변수 섹션이있는 메서드에 N 인수 목록 적용 (여기서 N <M)은를 사용하여 명시 적으로 _또는 FunctionN[..]. 이것은 안전 기능입니다. 배경은 Scala References의 Scala 2.0에 대한 변경 노트를 참조하십시오.

카레 기능

커리 함수 (또는 간단히 함수를 반환하는 함수)는 N 인수 목록에 더 쉽게 적용 할 수 있습니다.

val f = (a: Int) => (b: Int) => (c: Int) => a + b + c
val g = f(1)(2)

이 사소한 편의는 때때로 가치가 있습니다. 함수는 유형 매개 변수가 될 수 없으므로 경우에 따라 메서드가 필요합니다.

두 번째 예는 함수를 반환하는 하나의 매개 변수 섹션 메서드 인 하이브리드입니다.

다단계 계산

카레 기능이 유용한 다른 곳은 어디입니까? 다음은 항상 나타나는 패턴입니다.

def v(t: Double, k: Double): Double = {
   // expensive computation based only on t
   val ft = f(t)

   g(ft, k)
}

v(1, 1); v(1, 2);

결과를 f(t)어떻게 공유 할 수 있습니까? 일반적인 해결책은 다음의 벡터화 된 버전을 제공하는 것입니다 v.

def v(t: Double, ks: Seq[Double]: Seq[Double] = {
   val ft = f(t)
   ks map {k => g(ft, k)}
}

추한! 우리는 관련이없는 문제를 얽히게 한 - 계산 g(f(t), k)의 순서를 통해 매핑 ks.

val v = { (t: Double) =>
   val ft = f(t)
   (k: Double) => g(ft, k)       
}
val t = 1
val ks = Seq(1, 2)
val vs = ks map (v(t))

함수를 반환하는 메서드를 사용할 수도 있습니다. 이 경우 좀 더 읽기 쉽습니다.

def v(t:Double): Double => Double = {
   val ft = f(t)
   (k: Double) => g(ft, k)       
}

그러나 여러 매개 변수 섹션이있는 메소드로 동일한 작업을 시도하면 멈춰 있습니다.

def v(t: Double)(k: Double): Double = {
                ^
                `-- Can't insert computation here!
}

3
훌륭한 답변; 하나 이상의 찬성표가 있었으면 좋겠어요. 저는 스타일 가이드를 소화하고 적용 할 것입니다. 내가 성공하면이게 선택된 답입니다…
davetron5000

1
루프 예제를 다음과 같이 수정할 수 있습니다.def loop[A](n: Int)(body: => A): Unit = (0 until n) foreach (n => body)
Omer van Kloeten 2011

이 컴파일되지 않습니다 :val f: (a: Int) => (b: Int) => (c: Int) = a + b + c
nilskp

"N 인수 목록을 M 매개 변수 섹션 (여기서 N <M)이있는 메소드에 적용하면 명시 적으로 _를 사용하여 함수로 변환하거나 예상 유형이 FunctionN [..] 인 함축적으로 변환 할 수 있습니다." <br/> FunctionX [..]가 아니어야합니다. 여기서 X = MN?
stackoverflower

"이것은 컴파일되지 않습니다 : val f : (a : Int) => (b : Int) => (c : Int) = a + b + c" "f : (a : Int) = > (b : Int) => (c : Int) "는 올바른 구문입니다. 아마도 retronym은 "f : Int => Int => Int => Int"를 의미했을 것입니다. =>가 오른쪽 연관이므로 실제로는 "f : Int => (Int => (Int => Int))"입니다. 따라서 f (1) (2)는 Int => Int 유형입니다 (즉, f 유형에서 가장 안쪽 비트)
stackoverflower

16

메서드가 아닌 함수 만 카레 할 수 있습니다. add메서드이므로 _함수로 강제 변환하려면이 필요합니다. add2함수를 반환하므로는 _불필요 할뿐만 아니라 여기서 의미가 없습니다.

다른 방법과 기능 (예 : JVM의 관점에서) 얼마나 고려, 스칼라는 꽤 좋은 작업 그들 사이의 경계를 모호하게하고 대부분의 경우에 "옳은 일을"일을 수행하지만 거기에 있다 차이, 그리고 때때로 당신은 단지 필요 그것에 대해 알고 있습니다.


이것은 의미가 있습니다. 그러면 def add (a : Int) (b : Int) 형식을 무엇이라고 부릅니까? def와 def add (a : Int, b : Int)의 차이점을 설명하는 용어 / 구는 무엇입니까?
davetron5000

@ davetron5000 첫 번째는 여러 매개 변수 목록이있는 메소드이고 두 번째는 하나의 매개 변수 목록이있는 메소드입니다.
Jesper

5

두 개의 매개 변수가 def add(a: Int)(b: Int): Int있는 메소드를 정의하기 만하면 그 차이를 파악하는 것이 도움이 될 것 입니다.이 매개 변수 만 두 개의 매개 변수 목록으로 그룹화됩니다 (다른 주석에서 그 결과를 참조하십시오). 사실, 그 방법은 int add(int a, int a)자바 (Scala가 아님!)에 관한 것입니다. 을 작성할 때 add(5)_그것은 단지 함수 리터럴 일뿐입니다 { b: Int => add(1)(b) }. 반면에 add2(a: Int) = { b: Int => a + b }매개 변수가 하나만있는 메소드를 정의하면 Java의 경우 scala.Function add2(int a). add2(1)Scala로 작성 하는 것은 (함수 리터럴과 반대되는) 단순한 메서드 호출 일뿐입니다.

또한 모든 매개 변수를 즉시 제공하는 경우 add보다 (잠재적으로) 오버 헤드가 적습니다 add2. JVM 수준에서로 add(5)(6)변환되는 것처럼 개체가 생성 add(5, 6)되지 않습니다 Function. 반면 에은 add2(5)(6)먼저를 포함하는 Function객체를 5만든 다음이를 호출 apply(6)합니다.

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