그루비는 부분 애플리케이션을 '커링'이라고 부릅니까?


15

Groovy는 '카레'라는 개념을 가지고 있습니다. 다음은 Wiki의 예입니다.

def divide = { a, b -> a / b }

def halver = divide.rcurry(2)

assert halver(8) == 4

여기서 무슨 일이 일어나고 있는지에 대한 나의 이해는 오른쪽 인수 divide가 가치 2에 묶여 있다는 것입니다. 이것은 부분 적용의 형태처럼 보입니다.

커링이라는 용어는 일반적으로 일련의 인수를 취하는 함수를 하나의 인수 만 사용하고 다른 함수를 반환하는 함수로 변환하는 것을 의미합니다. 예를 들어 다음은 curryHaskell 의 함수 유형입니다 .

curry :: ((a, b) -> c) -> (a -> (b -> c))

하스켈을 사용하지 않은 사람들을 위해 a, b그리고 c모든 일반 매개 변수입니다. curry두 개의 인수로 함수를 취하고,받는 함수 반환 a과에서 함수를 반환 b하는 방법에 대해 c. 더 명확하게하기 위해 유형에 대괄호 쌍을 추가했습니다.

groovy 예제에서 무슨 일이 일어나고 있는지 잘못 이해 했습니까? 아니면 부분적으로 이름이 잘못 지정된 것입니까? 또는 실제로 두 가지 모두를 수행합니다. 즉 divide, 커리 함수 로 변환 한 다음 2이 새 함수에 부분적으로 적용 합니다.


답변:


14

Groovy의 구현은 curry장면 뒤에서조차 실제로 어떤 시점에서도 카레하지 않습니다. 본질적으로 부분 적용과 동일합니다.

curry, rcurryncurry방법은 반환 CurriedClosure객체 바인딩 인수를 보유하고 있습니다. 또한 getUncurriedArguments바인딩 된 인수와 함께 전달 된 인수의 구성을 반환 하는 메서드 (인수가 아닌 함수를 카레 함)가 있습니다.

클로저가 호출되면 궁극적으로 invokeMethod메소드를MetaClassImpl 호출하여 호출 객체가의 인스턴스인지 여부를 명시 적으로 확인합니다 CurriedClosure. 그렇다면 위에서 언급 한 내용 getUncurriedArguments을 사용 하여 전체 인수 배열을 작성하여 적용하십시오.

if (objectClass == CurriedClosure.class) {
    // ...
    final Object[] curriedArguments = cc.getUncurriedArguments(arguments);
    // [Ed: Yes, you read that right, curried = uncurried. :) ]
    // ...
    return ownerMetaClass.invokeMethod(owner, methodName, curriedArguments);
}

위의 혼란스럽고 다소 일관성이없는 명명법을 바탕으로,이 글을 쓴 사람은 개념에 대한 이해가 좋았지 만, 많은 똑똑한 사람들처럼 약간의 서두르고 부분적인 적용으로 카레가 부풀려 졌을 것입니다. 불행히도 이것은 이해할 만합니다 (폴 킹의 답변 참조). 이전 버전과의 호환성을 유지하지 않으면이 문제를 해결하기가 어렵습니다.

내가 제안한 한 가지 해결책curry인수가 전달되지 않을 때 실제로 카레를 수행하고 새로운 partial함수 를 선호하는 인수로 메소드 호출을 더 이상 사용 하지 않도록 메소드 를 오버로드하는 것입니다. 이것은 조금 이상하게 보일지 모르지만, 인수가 0 인 부분 응용 프로그램을 사용할 이유가 없기 때문에 이전 버전과의 호환성을 최대화 할 것입니다. 함수가 실제로있는 동안 올바른 카레를 위해 다른 이름이 지정된 새로운 기능을 갖는 (IMHO) 더 추악한 상황을 피하십시오. named curry는 다르고 혼란스럽게 비슷한 것을합니다.

전화 결과는 curry실제 카레와 완전히 다릅니다. 실제로 함수를 커리면 다음과 같이 쓸 수 있습니다.

def add = { x, y -> x + y }
def addCurried = add.curry()   // should work like { x -> { y -> x + y } }
def add1 = addCurried(1)       // should work like { y -> 1 + y }
assert add1(1) == 2 

… 그리고 작동 addCurried해야 하기 때문에 작동 { x -> { y -> x + y } }합니다. 대신 런타임 예외가 발생하고 내부에서 약간 죽습니다.


1
나는 인수 기능에 rcurry 및 ncurry가> 2이 정말 무두질뿐만 아니라 일부 응용 프로그램이 있음을 입증 생각
JK.

@jk 사실, 마지막에 언급했듯이 인수 == 2 인 함수에 대해 설명 할 수 있습니다. :)
Jordan Gray

3
@matcauthon 엄밀히 말하면, 카레의 "목적"은 많은 인수를 가진 함수를 각각 하나의 인수를 가진 중첩 된 함수 체인으로 변환하는 것입니다. 나는 당신이 요구하는 것이 당신이 카레를 사용 하기원하는 실제적인 이유라고 생각합니다 . 이것은 LISP 나 Haskell보다 Groovy에서 정당화하기가 조금 더 어렵습니다. 요점은, 아마도 대부분의 시간을 사용하고 싶을 부분은 카레가 아닌 부분 적용입니다.
Jordan Gray

4
+1and you die a little inside
Thomas Eding

1
와우, 나는 당신의 답을 읽은 후에 커리가 훨씬 잘 이해합니다 : +1 그리고 감사합니다! 이 질문과 관련하여 "부분적인 적용으로 카레를 con 다"고 말했습니다.
GlenPeterson

3

그루비 카레는 실제로 두 가지 이상의 인수가있는 함수를 고려할 때 부분적으로 적용되는 것이 분명하다고 생각합니다. 치다

f :: (a,b,c) -> d

카레 형태는

fcurried :: a -> b -> c -> d

그러나 groovy의 카레는 다음과 같은 것을 반환합니다 (1 인수 x로 호출되었다고 가정)

fgroovy :: (b,c) -> d 

x에 고정 된 값으로 f를 호출합니다.

즉, groovy의 카레는 N-1 인수를 가진 함수를 반환 할 수 있지만, 카레 함수는 1 개의 인수 만 갖으므로, groovy는 카레로 카레를 수행 할 수 없습니다


2

Groovy는 부분 적용에 유사한 명명법을 사용하는 수많은 다른 비 순수 FP 언어에서 카레 분석법의 명명법을 빌렸다. Groovy에 포함시키기 위해 제안 된 몇 가지 "실제"카레 구현이 있습니다. 그들에 대해 읽을 수있는 좋은 실이 여기 있습니다 :

http://groovy.markmail.org/thread/c4ycxdzm3ack6xxb

기존 기능은 어떤 형태로 유지되며 새로운 메소드의 이름을 지정할 때 호출 할 때 이전 버전과의 호환성이 고려됩니다. 따라서이 단계에서 새로운 / 오래된 메소드의 최종 이름이 무엇인지 말할 수 없습니다. 있다. 아마도 이름에 대한 타협이지만 우리는 보게 될 것입니다.

대부분의 OO 프로그래머들에게는 두 용어 (커링 및 부분 적용)의 구별이 대부분 학문적입니다. 그러나 일단 익숙해지면 코드를 유지 관리하는 사람 이이 스타일의 코딩을 읽도록 훈련되면 포인트 프리 또는 암묵적 스타일 프로그래밍 ( "실제"카레 링 지원)을 통해 특정 종류의 알고리즘을보다 간결하게 표현할 수 있습니다 어떤 경우에는 더 우아하게. 여기에는 분명히 "아름다움이 보는 사람의 눈에있다"는 것이 있지만 두 스타일을 모두 지원할 수있는 능력은 Groovy의 본질 (OO / FP, 정적 / 동적, 클래스 / 스크립트 등)과 동일합니다.


1

이 정의가 IBM에서 발견 된 경우 :

카레라는 용어는 부분 기능의 개념을 개발 한 수학자 Haskell Curry에서 가져온 것입니다. Currying은 여러 개의 인수를 여러 개의 인수를 취하는 함수로 가져 와서 나머지 인수를 가져와 결과를 반환하는 새로운 함수를 만드는 것을 말합니다.

halver새로운 (커 리드 된) 함수 (또는 클로저)는 이제 하나의 매개 변수 만 사용합니다. 전화 halver(10)하면 5가됩니다.

따라서 n-1 인수를 가진 함수에서 n 인수를 가진 함수를 변환합니다. 카레가하는 일을 하스켈 예제에서도 마찬가지입니다.


4
IBM의 정의가 올바르지 않습니다. 그들이 커링으로 정의하는 것은 실제로 부분 함수 응용 프로그램으로, 함수의 인수를 묶어 더 작은 arity로 함수를 만듭니다. Currying은 여러 인수를받는 함수를 각각 하나의 인수를 취하는 함수 체인으로 변환합니다.
Jordan Gray

1
위키 백과의 정의에서 IBM과 동일합니다 . 수학 및 컴퓨터 과학에서 카레는 여러 인수 (또는 n- 튜플의 인수)를 취하는 함수를 다음과 같이 호출 할 수있는 방식으로 변환하는 기술입니다. 각각 단일 인수를 갖는 함수 체인 (부분 응용 프로그램). Groovy rcurry는 하나의 인수를 갖는 함수를 가진 함수 ( 두 개의 인수를 가진 )를 함수 (하나의 인수 만있는)로 변환합니다. 결과 함수를 얻기 위해 기본 함수에 대한 인수와 함께 curry 함수를 연결했습니다.
matcauthon 2016 년

3
아니, 위키피디아 정의가 다르다-부분적인 응용은 커리 함수를 호출 할 때
-groovy가하는 것

6
@jk가 맞습니다. Wikipedia 설명을 다시 읽으면 리턴되는 것은 인수가있는 함수가 아니라 각각 하나의 인수가있는 함수 체인이라는 것을 알 수 n - 1있습니다. 내 대답 끝에있는 예를 참조하십시오. 차이점에 대한 자세한 내용은이 기사의 뒷부분을 참조하십시오. en.wikipedia.org/wiki/…
Jordan Gray

4
매우 중요합니다. 믿어주세요. 다시 말하지만, 내 대답 끝에있는 코드는 올바른 구현이 어떻게 작동하는지 보여줍니다. 한 가지 주장은 필요하지 않습니다. 현재 구현은 실제로 이름을 eg로 지정해야합니다 partial.
Jordan Gray
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.