[scala] 스칼라 커링과 부분적으로 적용된 함수

여기에 카레와 부분적으로 적용된 기능이 무엇인지 에 대한 몇 가지 질문이 있다는 것을 알고 있지만 어떻게 다른지 묻고 있습니다. 간단한 예로서 짝수를 찾기위한 카레 함수가 있습니다.

def filter(xs: List[Int], p: Int => Boolean): List[Int] =
   if (xs.isEmpty) xs
   else if (p(xs.head)) xs.head :: filter(xs.tail, p)
   else filter(xs.tail, p)

def modN(n: Int)(x: Int) = ((x % n) == 0)

따라서 이것을 사용하려면 다음을 작성할 수 있습니다.

val nums = List(1,2,3,4,5,6,7,8)
println(filter(nums, modN(2))

반환 : List(2,4,6,8). 그러나 나는 이런 식으로 똑같은 일을 할 수 있음을 발견했습니다.

def modN(n: Int, x: Int) = ((x % n) == 0)

val p = modN(2, _: Int)
println(filter(nums, p))

또한 반환 : List(2,4,6,8).

그래서 내 질문은, 둘의 주요 차이점은 무엇이며 언제 다른 하나를 사용합니까? 하나가 다른 것에 사용되는 이유를 보여주기에는 너무 단순한 예입니까?



답변

의미 론적 차이는 Plasty Grove에 연결된 답변 에서 상당히 잘 설명되었습니다 .

그러나 기능면에서 큰 차이는 없습니다. 이를 확인하기 위해 몇 가지 예를 살펴 보겠습니다. 첫째, 정상적인 기능 :

scala> def modN(n: Int, x: Int): Boolean = ((x % n) == 0)
scala> modN(5, _ : Int)
res0: Int => Boolean = <function1>

그래서 우리 는 이미 첫 번째 정수를 주었기 때문에 <function1>를 취하는 부분적으로 적용됩니다 Int. 여태까지는 그런대로 잘됐다. 이제 카레로 :

scala> def modNCurried(n: Int)(x: Int): Boolean = ((x % n) == 0)

이 표기법을 사용하면 다음 작업이 순진하게 예상됩니다.

scala> modNCurried(5)
<console>:9: error: missing arguments for method modN;
follow this method with `_' if you want to treat it as a partially applied function
          modNCurried(5)

따라서 다중 매개 변수 목록 표기법은 실제로 즉시 커리 함수를 생성하는 것처럼 보이지 않지만 (불필요한 오버 헤드를 피하기 위해) 사용자가 커리를 원한다고 명시 적으로 명시하기를 기다립니다 (표기법에는 다른 장점 도 있습니다).

scala> modNCurried(5) _
res24: Int => Boolean = <function1>

이전에 얻은 것과 똑같기 때문에 표기법을 제외하고는 차이가 없습니다. 또 다른 예:

scala> modN _
res35: (Int, Int) => Boolean = <function2>

scala> modNCurried _
res36: Int => (Int => Boolean) = <function1>

“일반”함수를 부분적으로 적용하면 모든 매개 변수를받는 함수가 생성되는 반면, 여러 매개 변수 목록이있는 함수를 부분적으로 적용하면 매개 변수 목록 당 하나씩 함수 체인이 생성 되고 모두 새 함수를 반환하는 방법을 보여줍니다.

scala> def foo(a:Int, b:Int)(x:Int)(y:Int): Int = a * b + x - y
scala> foo _
res42: (Int, Int) => Int => (Int => Int) = <function2>

scala> res42(5)
<console>:10: error: not enough arguments for method apply: (v1: Int, v2: Int)Int => (Int => Int) in trait Function2.
Unspecified value parameter v2.

보시다시피 첫 번째 매개 변수 목록 foo에는 두 개의 매개 변수가 있으므로 커리 체인의 첫 번째 함수에는 두 개의 매개 변수가 있습니다.


요약하자면 부분적으로 적용된 함수는 기능 측면에서 실제로 다른 형태의 커리 함수가 아닙니다. 모든 함수를 카레로 변환 할 수 있다는 점을 고려하면 쉽게 확인할 수 있습니다.

scala> (modN _).curried
res45: Int => (Int => Boolean) = <function1

scala> modNCurried _
res46: Int => (Int => Boolean) = <function1>

Post Scriptum

참고 : 예제 println(filter(nums, modN(2))가 밑줄없이 작동 하는 이유 modN(2)는 Scala 컴파일러가 단순히 프로그래머의 편의를 위해 밑줄을 가정하기 때문인 것 같습니다.


추가 : @asflierl이 올바르게 지적했듯이 Scala는 “일반”함수를 부분적으로 적용 할 때 유형을 추론 할 수없는 것 같습니다.

scala> modN(5, _)
<console>:9: error: missing parameter type for expanded function ((x$1) => modN(5, x$1))

이 정보는 다중 매개 변수 목록 표기법을 사용하여 작성된 함수에 사용할 수 있습니다.

scala> modNCurried(5) _
res3: Int => Boolean = <function1>

이 답변 은 이것이 얼마나 유용 할 수 있는지 보여줍니다.


답변

커링은 튜플과 관련 이 있습니다. 튜플 인수를받는 함수를 n 개의 개별 인수를받는 함수로 바꾸고 그 반대의 경우도 마찬가지 입니다. 이것이 카레를 완전히 지원하지 않는 언어에서도 카레와 부분 적용을 구별하는 열쇠임을 기억하십시오.

curry :: ((a, b) -> c) -> a -> b -> c
   -- curry converts a function that takes all args in a tuple
   -- into one that takes separate arguments

uncurry :: (a -> b -> c) -> (a, b) -> c
   -- uncurry converts a function of separate args into a function on pairs.

부분 적용은 일부 인수에 함수적용하여 나머지 인수에 대한 새 함수를 생성하는 기능입니다 .

커링이 튜플과 관련된 변형이라고 생각하면 기억하기 쉽습니다.

기본적으로 카레 처리되는 언어 (예 : Haskell)에서는 차이점이 분명합니다. 실제로 튜플에서 인수를 전달하려면 무언가를해야합니다. 그러나 Scala를 포함한 대부분의 다른 언어는 기본적으로 uncurried입니다. 모든 arg는 튜플로 전달되므로 curry / uncurry는 훨씬 덜 유용하고 덜 명확합니다. 그리고 사람들은 카레 기능을 쉽게 표현할 수 없기 때문에 부분 적용과 카레가 같은 것이라고 생각하게됩니다!


답변

다 변수 기능 :

def modN(n: Int, x: Int) = ((x % n) == 0)

카레 (또는 카레 기능) :

def modNCurried(n: Int)(x: Int) = ((x % n) == 0)

따라서 카레에 필적하는 부분적으로 적용된 기능이 아닙니다. 다 변수 함수입니다. 부분적으로 적용된 함수와 비교할 수있는 것은 부분적으로 적용된 함수와 동일한 매개 변수 목록이있는 함수 인 curried 함수의 호출 결과입니다.


답변

마지막 요점을 명확히하기 위해

추가 : @asflierl이 올바르게 지적했듯이 Scala는 “일반”함수를 부분적으로 적용 할 때 유형을 추론 할 수없는 것 같습니다.

Scala는 모든 매개 변수가 와일드 카드 인 경우 유형을 추론 할 수 있지만 일부는 지정되고 일부는 지정되지 않은 경우에는 그렇지 않습니다.

scala> modN(_,_)
res38: (Int, Int) => Boolean = <function2>

scala> modN(1,_)
<console>:13: error: missing parameter type for expanded function ((x$1) => modN(1, x$1))
       modN(1,_)
              ^


답변

지금까지 찾을 수있는 가장 좋은 설명 : https://dzone.com/articles/difference-between-currying-amp-partially-applied

Currying : 여러 인수가있는 함수를 단일 인수 함수 체인으로 분해합니다. Scala는 함수를 다른 함수에 인수로 전달할 수 있습니다.

함수의 부분적 적용 : 선언에있는 것보다 적은 인수를 함수에 전달합니다. Scala는 함수에 더 적은 인수를 제공 할 때 예외를 발생시키지 않고 단순히 적용하고 전달해야하는 나머지 인수가있는 새 함수를 반환합니다.


답변