Scala에서 커링하는 두 가지 방법; 각각의 사용 사례는 무엇입니까?
내가 유지하는 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)_
스타일 가이드는 그것들이 분명히 같지 않은데도 동일하다고 잘못 암시합니다. 가이드는 만든 카레 기능에 대해 지적하려고 노력하고 있으며, 두 번째 형태는 "책별"카레 링이 아니지만 여전히 첫 번째 형태와 매우 유사합니다 (비록 필요하지 않기 때문에 사용하기가 더 쉽습니다. _
)
이 양식을 사용하는 사람들로부터 한 양식을 다른 양식보다 사용할 때에 대한 합의는 무엇입니까?
다중 매개 변수 목록 방법
유형 추론
여러 매개 변수 섹션이있는 메서드는 첫 번째 섹션의 매개 변수를 사용하여 후속 섹션의 인수에 대해 예상되는 형식을 제공 할 형식 인수를 추론함으로써 로컬 형식 유추를 지원하는 데 사용할 수 있습니다. 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);
How can we share the result f(t)
? A common solution is to provide a vectorized version of v
:
def v(t: Double, ks: Seq[Double]: Seq[Double] = {
val ft = f(t)
ks map {k => g(ft, k)}
}
Ugly! We've entangled unrelated concerns -- calculating g(f(t), k)
and mapping over a sequence of 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))
We could also use a method that returns a function. In this case its a bit more readable:
def v(t:Double): Double => Double = {
val ft = f(t)
(k: Double) => g(ft, k)
}
But if we try to do the same with a method with multiple parameter sections, we get stuck:
def v(t: Double)(k: Double): Double = {
^
`-- Can't insert computation here!
}
You can curry only functions, not methods. add
is a method, so you need the _
to force its conversion to a function. add2
returns a function, so the _
is not only unnecessary but makes no sense here.
Considering how different methods and functions are (e.g. from the perspective of the JVM), Scala does a pretty good job blurring the line between them and doing "The Right Thing" in most cases, but there is a difference, and sometimes you just need to know about it.
I think it helps to grasp the differences if I add that with def add(a: Int)(b: Int): Int
you pretty much just define a method with two parameters, only those two parameters are grouped into two parameter lists (see the consequences of that in other comments). In fact, that method is just int add(int a, int a)
as far as Java (not Scala!) is concerned. When you write add(5)_
, that's just a function literal, a shorter form of { b: Int => add(1)(b) }
. On the other hand, with add2(a: Int) = { b: Int => a + b }
you define a method that has only one parameter, and for Java it will be scala.Function add2(int a)
. When you write add2(1)
in Scala it's just a plain method call (as opposed to a function literal).
Also note that add
has (potentially) less overhead than add2
has if you immediately provide all parameters. Like add(5)(6)
just translates to add(5, 6)
on the JVM level, no Function
object is created. On the other hand, add2(5)(6)
will first create a Function
object that encloses 5
, and then call apply(6)
on that.
'code' 카테고리의 다른 글
소수를 생성하는 가장 우아한 방법 (0) | 2020.10.05 |
---|---|
.gitignore에 # * # glob을 추가 하시겠습니까? (0) | 2020.10.05 |
C에서 새 디렉토리 만들기 (0) | 2020.10.05 |
IntelliJ IDEA의 모든 UI 요소에서 글꼴을 늘리는 방법은 무엇입니까? (0) | 2020.10.05 |
`http.NewRequest (…)`를 사용하여 URL 인코딩 된 POST 요청을 만듭니다. (0) | 2020.10.05 |