카레 또는 부분 적용의 특별한 점은 무엇입니까?


9

매일 함수형 프로그래밍에 대한 기사를 읽고 가능한 한 많은 연습을 적용하려고했습니다. 그러나 나는 카레 또는 부분 적용의 독특한 점을 이해하지 못합니다.

이 Groovy 코드를 예로 들어 보겠습니다.

def mul = { a, b -> a * b }
def tripler1 = mul.curry(3)
def tripler2 = { mul(3, it) }

나는 사이의 차이점이 무엇인지 이해가 안 tripler1tripler2. 둘 다 같지 않습니까? '커링'은 Groovy, Scala, Haskell 등과 같은 순수하거나 부분적인 기능 언어로 지원됩니다. 그러나 단순히 이름이 있거나 익명 인 다른 이름을 만들어서 동일한 작업 (왼쪽 카레, 오른쪽 카레, n- 카레 또는 부분 응용 프로그램)을 수행 할 수 있습니다. tripler2대부분의 언어 (예 : C)에서 매개 변수를 원래 기능 (예 :)으로 전달하는

여기에 뭔가 빠졌습니까? Grails 애플리케이션에 카레 링 및 부분 애플리케이션을 사용할 수있는 곳이 있지만, "어떻게 다른가요?"라는 질문을하기 때문에 주저하고 있습니다.

제발 깨달아 줘

편집 : 기본 응용 프로그램을 원래 함수로 전달하는 다른 함수를 작성 / 호출하는 것보다 부분 응용 프로그램 / 커링이 더 효율적이라고 말하는가?


1
누군가 "curry"또는 "currying"태그를 생성 할 수 있습니까?
Vigneshwaran

C는 어떻게 카레?
Giorgio

이것은 아마도 부분 응용 프로그램 프로그래머
. stackexchange.com/questions/152868/…

1
@ Vigneshwaran : AFAIK 카레를 지원하는 언어로 다른 기능을 만들 필요는 없습니다. 예를 들어, Haskell에서 f x y = x + y이는 f하나의 int 매개 변수를 취하는 함수임을 의미합니다 . f x(에 f적용됨 x) 의 결과 는 하나의 int 매개 변수를 사용하는 함수입니다. 결과 f x y(또는 (f x) yf x적용됨 y)는 입력 매개 변수를 사용하지 않고 축소하여 평가하는 식입니다 x + y.
Giorgio

1
같은 작업을 수행 할 수 있지만 C를 사용하는 노력의 양은 기본 동작 인 haskell과 같은 언어에서보다 훨씬 더 고통스럽고 비효율적입니다.
Daniel Gratzer

답변:


8

카레는 n 개의 입력을 각각 1 개의 입력을받는 n 개의 함수로 바꾸는 기능을 돌리거나 나타내는 것입니다. 부분 적용은 일부 입력을 함수에 고정하는 것입니다.

부분 응용 프로그램의 동기는 주로 고차 함수 라이브러리를보다 쉽게 ​​작성할 수 있도록하는 것입니다. 예를 들어 C ++ STL 의 알고리즘은 모두 술어 또는 단항 함수를 사용하므로 bind1st를 사용하면 라이브러리 사용자가 단항이 아닌 함수를 값이 바인드 된 상태로 연결할 수 있습니다. 따라서 라이브러리 라이터는 이진 버전을 제공하기 위해 단항 함수를 사용하는 모든 알고리즘에 대해 오버로드 된 함수를 제공 할 필요가 없습니다.

Currying 자체는 원하는 곳 어디에서나 부분적으로 적용 할 수 있기 때문에 유용합니다. 즉 bind1st부분적으로 적용 하는 것과 같은 기능이 더 이상 필요하지 않습니다 .


이다 currying뭔가 그루비하는 특정 또는 언어에 걸쳐 적용은?
수륙 양용 비행기

@foampile은 여러 언어에 적용 할 수 있지만 아이러니하게도 그루비 카레는 실제로 프로그래머를
.

@jk. 커링 / 부분 응용 프로그램이 다른 함수를 만들고 호출하는 것보다 효율적이라고 말하는가?
Vigneshwaran

2
@ Vigneshwaran-반드시 성능이 높지는 않지만 프로그래머의 시간면에서 확실히 더 효율적입니다. 또한 커링은 많은 기능적 언어에서 지원되지만 일반적으로 OO 또는 절차 적 언어에서는 지원되지 않습니다. (또는 적어도 언어 자체는 아닙니다.)
Stephen C

6

그러나 대부분의 언어에서 (트리머 2와 같은) 원래 함수로 매개 변수를 전달하는 다른 명명 된 또는 익명 함수 또는 클로저를 작성하여 동일한 작업 (왼쪽 카레, 오른쪽 카레, n 카레 또는 부분 응용 프로그램)을 수행 할 수 있습니다 ( 심지어 C.)

그리고 옵티마이 저는이를보고 즉시 이해할 수있는 것으로 넘어갑니다. 카레는 최종 사용자에게는 유용한 기술이지만 언어 디자인의 관점에서 훨씬 더 유리합니다. 그건 정말 단항 모든 방법을 처리하는 좋은 다른 방법이 될 수 있습니다.A -> BB

고차 함수를 처리하기 위해 작성해야하는 메소드를 단순화합니다. 언어의 정적 분석 및 최적화에는 알려진 방식으로 작동하는 경로가 하나만 있습니다. 매개 변수 바인딩은 후프 가이 일반적인 동작을 수행하도록 요구하지 않고 디자인에서 벗어납니다.


6

@jk로. 카레는 코드를 더 일반적으로 만드는 데 도움이 될 수 있습니다.

예를 들어, Haskell에 다음 세 가지 기능이 있다고 가정합니다.

> let q a b = (2 + a) * b

> let r g = g 3

> let f a b = b (a 1)

여기서 함수 f는 두 개의 함수를 인수로 취하여 1첫 번째 함수에 전달하고 첫 번째 호출의 결과를 두 번째 함수에 전달합니다.

우리가 있었던 경우 호출 f사용 qr인수로, 효과적으로 일을 할 것입니다 :

> r (q 1)

여기서 q인가 될 1과 (와 같은 다른 함수를 리턴 q카레된다); 이 반환 된 함수는 r인수로 전달되도록 인수로 전달됩니다 3. 이 결과는 값이 9됩니다.

이제 두 가지 다른 기능이 있다고 가정 해 봅시다.

> let s a = 3 * a

> let t a = 4 + a

우리는 이것들을 f또한 전달할 수 있고 우리의 주장이 또는 인지에 따라 7또는 의 가치를 얻을 수 있습니다. 이러한 함수는 모두 함수가 아닌 값을 반환하기 때문에 또는 에서 부분 적용이 수행되지 않습니다 .15s tt sf s tf t s

우리는 쓴 경우 fq하고 r대신 일부 응용 프로그램, 예를 들면의 마음에 우리는 람다 (익명 함수)를 사용하고 있습니다 :

> let f' a b = b (\x -> a 1 x)

그러나 이것은의 일반성을 제한했을 것입니다 f'. f인수로 호출 할 수 qr또는 s하고 t있지만 f'에서만 호출 할 수 qr- f' s tf' t s오류가 모두 결과.

인수가 두 개 이상인 / 쌍 f'으로 호출 된 경우 여전히 부분적으로에 적용됩니다 .q'r'q'q'f'

또는 내부 대신 q외부를 감쌀 수 f있지만 불쾌한 중첩 된 람다가 남습니다.

f (\x -> (\y -> q x y)) r

본질적으로 카레 q가 처음에 있었던 것입니다!


당신은 내 눈을 뜨었다. 당신의 대답은 커리 / 부분 적용 함수가 원래 함수에 인수를 전달하는 새로운 함수를 만드는 것과 어떻게 다른지 깨달았습니다. 1. F처럼 (약 curryied / paed 기능을 전달 (q.curry (2)) 단지 임시 사용을 위해 불필요하게 별도의 기능을 만드는 것보다 깔끔한 있습니다 (기능 언어 그루비처럼).
Vigneshwaran

2. 제 질문에 "C에서도 똑같이 할 수 있습니다"라고 말했습니다. 예, 그러나 기능이없는 언어에서는 함수를 데이터로 전달할 수없고 매개 변수를 원래로 전달하는 별도의 함수를 작성하는 것이 카레 / pa의 이점을 모두 가지고 있지는 않습니다.
Vigneshwaran

Groovy는 Haskell이 지원하는 일반화 유형을 지원하지 않습니다. 나는 써야했다 def f = { a, b -> b a.curry(1) }만드는 f q, r작업과에 def f = { a, b -> b a(1) }def f = { a, b -> b a.curry(1)() }에 대한 f s, t작업에. 모든 매개 변수를 전달하거나 카레라고 명시 적으로 말해야합니다. :(
Vigneshwaran

2
@ Vigneshwaran : 그렇습니다. Haskell과 카레 는 매우 잘 어울린다고 말하는 것이 안전합니다 . ;] Haskell에서 함수는 기본적으로 (정확한 정의로) 카레되며 공백은 함수 응용 프로그램을 나타내므로 f x y많은 언어가 f(x)(y)아닌을 쓰는 것을 의미합니다 f(x, y). 아마도 코드가 다음 q과 같이 호출되도록 작성하면 Groovy에서 코드가 작동 할 것입니다 q(1)(2).
CA McCann

1
@Vigneshwaran 다행 내가 도와 줄 수있어! 부분적으로 적용한다고 명시 적으로 말해야하는 것에 대한 고통을 느낍니다. Clojure에서는 (partial f a b ...)Haskell에 익숙해 져서 다른 언어로 프로그래밍 할 때 적절한 카레를 많이 그리워 하지 않습니다 (최근에 F #에서 지원했지만 고맙게도 지원합니다).
paul

3

부분 적용에 대한 두 가지 핵심 사항이 있습니다. 첫 번째는 구문 / 편의입니다. @ jk에서 언급했듯이 일부 정의는 읽고 쓰기가 더 쉽고 짧아집니다. ( 이 얼마나 멋진 지에 대한 자세한 내용은 Pointfree 프로그래밍 을 확인하십시오 !)

@telastyn이 언급했듯이 두 번째는 함수 모델에 관한 것이며 단순히 편리하지는 않습니다. Haskell 버전에서는 부분 응용 프로그램을 사용하는 다른 언어에 익숙하지 않기 때문에 예제를 얻을 수 있습니다. 모든 함수에는 단일 인수가 사용됩니다. 그렇습니다.

(:) :: a -> [a] -> [a]

하나의 주장을 취하십시오. 함수 타입 생성자의 연관성 때문에 ->위의 내용은 다음과 같습니다.

(:) :: a -> ([a] -> [a])

이는 함수를 가져 와서 함수를 a반환하는 함수 [a] -> [a]입니다.

이를 통해 다음과 같은 함수를 작성할 수 있습니다.

($) :: (a -> b) -> a -> b

이는 적용 할 수 있는 해당 유형의 인수 기능을. 심지어 미친 것들 :

f :: (t, t1) -> t -> t1 -> (t2 -> t3 -> (t, t1)) -> t2 -> t3 -> [(t, t1)]
f q r s t u v = q : (r, s) : [t u v]

f' :: () -> Char -> (t2 -> t3 -> ((), Char)) -> t2 -> t3 -> [((), Char)]
f' = f $ ((), 'a')  -- <== works fine

좋습니다, 그래서 그것은 고안된 예입니다. 그러나 더 유용한 방법은이 메소드를 포함 하는 Applicative type 클래스 와 관련이 있습니다.

(<*>) :: Applicative f => f (a -> b) -> f a -> f b

보시다시피 $, Applicative f비트를 제거하는 경우 와 유형이 동일 하며 실제로이 클래스는 컨텍스트에서 함수 응용 프로그램을 설명합니다. 따라서 정상적인 기능 적용 대신 :

ghci> map (+3) [1..5]  
[4,5,6,7,8]

응용 문맥에서 함수를 적용 할 수 있습니다. 예를 들어, 무언가가 존재하거나 누락되었을 수있는 어쩌면 문맥에서 :

ghci> Just map <*> Just (+3) <*> Just [1..5]
Just [4,5,6,7,8]

ghci> Just map <*> Nothing <*> Just [1..5]
Nothing

이제 정말 멋진 부분은 실용적 타입 클래스는 언급하지 않는다는 것입니다 무엇을 하나 개 이상의 인수의 기능에 대해 - 그럼에도 불구하고, 그것은, 그들과 거래를 추천 인수도 기능 할 수 있습니다 f:

fA' :: Maybe (() -> Char -> (t2 -> t3 -> ((), Char)) -> t2 -> t3 -> [((), Char)])
fA' = Just f <*> Just ((), 'a')

내가 아는 한, 일반적인 형태의 응용 유형 클래스는 부분 적용에 대한 개념이 없으면 불가능합니다. 언어는 일부 응용 프로그램이 부족한 경우, 당신은, 물론 - (거기 프로그래밍 전문가에게! 내가 틀렸다면 수정하시기 바랍니다) 는, 그것은 똑같지는 않습니다 ... 어떤 형태로 그것을 구축하지만, ? :)


1
Applicative카레 또는 부분 적용없이 사용 fzip :: (f a, f b) -> f (a, b)합니다. 고차 함수를 사용하는 언어에서는 카레 및 부분 응용 프로그램을 functor의 컨텍스트로 들어 올릴 수 있습니다 (<*>). 고차 함수가 없으면 fmap모든 것이 쓸모가 없습니다.
CA McCann

의견을 보내 주셔서 감사합니다! 나는이 대답으로 내 머리 위에 있다는 것을 알았습니다. 그래서 내가 잘못 말한 것입니까?

1
확실히 정신적으로 맞습니다. "일반적인 형태", "가능한"및 "부분적 적용의 개념"의 정의로 머리카락을 나누는 f <$> x <*> y것은 카레와 부분적 적용이 쉽게 작동하기 때문에 매력적인 관용적 스타일이 쉽게 작동 한다는 간단한 사실을 바꾸지 않을 것 입니다. 다시 말해, 즐거운 것이 여기서 가능한 것보다 더 중요 합니다.
CA McCann

함수형 프로그래밍의 코드 예제를 볼 때마다 정교한 농담이며 존재하지 않는다고 더 확신합니다.
Kieveli

1
@Kieveli 불행히도 그렇게 느낍니다. 기본 사항을 잘 이해하고 실습 할 수있는 훌륭한 자습서많이 있습니다 .
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.