여러 기사와 블로그에서 커리 함수에 대한 참조를 보았지만 좋은 설명 (또는 적어도 의미가있는 설명)을 찾을 수 없습니다!
add x y = x+y
(카레)에 다른 add (x, y)=x+y
(uncurried)
여러 기사와 블로그에서 커리 함수에 대한 참조를 보았지만 좋은 설명 (또는 적어도 의미가있는 설명)을 찾을 수 없습니다!
add x y = x+y
(카레)에 다른 add (x, y)=x+y
(uncurried)
답변:
카레는 여러 인수를 하나의 인수 만 취하는 일련의 함수로 나누는 함수를 세분화하는 것입니다. 다음은 JavaScript의 예입니다.
function add (a, b) {
return a + b;
}
add(3, 4); // returns 7
이것은 a와 b라는 두 개의 인수를 취하고 그 합을 반환하는 함수입니다. 우리는 이제이 기능을 카레 할 것입니다 :
function add (a) {
return function (b) {
return a + b;
}
}
이것은 하나의 인수 a를 취하는 함수이며 다른 인수 b를 취하는 함수를 반환하며 그 함수는 그 합계를 반환합니다.
add(3)(4);
var add3 = add(3);
add3(4);
첫 번째 문은 add (3, 4) 문과 같이 7을 반환합니다. 두 번째 명령문은 인수에 3을 추가하는 add3이라는 새 함수를 정의합니다. 이것은 일부 사람들이 폐쇄라고 할 수 있습니다. 세 번째 명령문은 add3 연산을 사용하여 3을 4로 더하고 결과적으로 7을 다시 생성합니다.
[1, 2, 3, 4, 5]
임의의 숫자를 곱하려는 숫자 목록이 있다고 가정하십시오. Haskell에서는 map (* 5) [1, 2, 3, 4, 5]
전체 목록에을 곱하여 목록 5
을 생성하도록 작성할 수 있습니다 [5, 10, 15, 20, 25]
.
map
는 하나의 인수 (목록의 요소) 만 취하는 함수 여야합니다. 수학적 개념으로서의 곱셈은 이항 연산입니다. 2 개의 인수가 필요합니다. 그러나 Haskell 에는이 답변 *
의 두 번째 버전과 비슷한 카레 기능 add
이 있습니다. 그 결과는 (* 5)
하나의 인수를 취하고 5를 곱한 함수이며 map과 함께 사용할 수 있습니다.
대수 함수에서 여러 인수 (또는 N 튜플과 동등한 하나의 인수)를 취하는 함수를 처리하는 것은 다소 우아하지 않지만 Moses Schönfinkel (및 독립적으로 Haskell Curry)이 입증했듯이 필요하지 않습니다. 하나의 인수를 취하는 함수가 필요합니다.
그렇다면 자연스럽게 표현한 것을 어떻게 다룰 f(x,y)
까요? 자, 당신은 그것을 f(x)(y)
- 와 동등한 것으로 간주하고 f(x)
그것을 호출 g
하고 함수이며, 그 함수를에 적용합니다 y
. 다시 말해서, 당신은 하나의 인수를 취하는 함수만을 가지고 있지만, 그 함수 중 일부는 다른 함수를 반환합니다 (또한 하나의 인수를받습니다 ;-).
평소와 같이 wikipedia 에는 이것에 대한 멋진 요약 항목이 있으며, 많은 유용한 포인터 (아마도 좋아하는 언어에 대한 포인터 포함);와 약간 더 엄격한 수학적 처리가 있습니다.
div :: Integral a => a -> a -> a
-여러 개의 화살표를 주목하십시오. "a를 a로 맵핑하는 함수 a"는 한 번의 읽기 ;-)입니다. 당신은 수 의 (단일) 튜플 인수 사용 div
& C를,하지만 하스켈 정말 안티 관용적 될 것이다.
구체적인 예는 다음과 같습니다.
물체에 작용하는 중력을 계산하는 함수가 있다고 가정하십시오. 수식을 모르는 경우 여기에서 찾을 수 있습니다 . 이 함수는 세 가지 필수 매개 변수를 인수로 사용합니다.
이제 지구상에 있으면이 행성의 물체에 대한 힘만 계산하려고합니다. 기능적 언어에서는 지구 덩어리를 기능에 전달한 다음 부분적으로 평가할 수 있습니다. 되돌아가는 것은 두 개의 인수 만 취하고 지구상의 물체의 중력을 계산하는 또 다른 함수입니다. 이것을 카레라고합니다.
Currying은 이전보다 한 가지 적은 인수를 취할 수 있도록 함수에 적용될 수있는 변환입니다.
예를 들어 F #에서는 다음과 같이 함수를 정의 할 수 있습니다.
let f x y z = x + y + z
여기서 함수 f는 매개 변수 x, y 및 z를 취하여 함께 합칩니다.
f 1 2 3
6을 반환합니다.
따라서 우리는 f :-에 대한 카레 함수를 정의 할 수 있습니다.
let curry f = fun x -> f x
여기서 'fun x-> fx'는 C #에서 x => f (x)에 해당하는 람다 함수입니다. 이 함수는 카레하려는 함수를 입력하고 단일 인수를 사용하고 첫 번째 인수가 입력 인수로 설정된 함수를 반환하는 함수를 반환합니다.
이전 예제를 사용하여 f의 카레를 얻을 수 있습니다.
let curryf = curry f
그런 다음 다음을 수행 할 수 있습니다.
let f1 = curryf 1
이는 f1 yz = 1 + y + z와 동등한 함수 f1을 제공합니다. 이것은 우리가 다음을 할 수 있음을 의미합니다.
f1 2 3
6을 반환합니다.
이 과정은 종종 다음과 같이 정의 할 수있는 '부분 기능 응용 프로그램'과 혼동됩니다.
let papply f x = f x
우리는 하나 이상의 매개 변수로 확장 할 수 있지만 다음과 같습니다.
let papply2 f x y = f x y
let papply3 f x y z = f x y z
etc.
부분 응용 프로그램은 함수와 매개 변수를 가져 와서 하나 이상의 매개 변수가 필요한 함수를 반환하며 이전 두 예제가 표준 F # 함수 정의에서 직접 구현되므로 이전 결과를 얻을 수 있습니다.
let f1 = f 1
f1 2 3
6의 결과를 반환합니다.
결론적으로:-
카레와 부분 함수 적용의 차이점은 다음과 같습니다.
Currying은 함수를 가져와 단일 인수를 허용하고 첫 번째 인수가 해당 인수로 설정된 지정된 함수를 반환하는 새 함수를 제공합니다. 이를 통해 여러 매개 변수가있는 함수를 일련의 단일 인수 함수로 나타낼 수 있습니다 . 예:-
let f x y z = x + y + z
let curryf = curry f
let f1 = curryf 1
let f2 = curryf 2
f1 2 3
6
f2 1 3
6
부분 함수 응용 프로그램이 더 직접적입니다. 함수와 하나 이상의 인수를 사용하여 지정된 n 개의 인수로 설정된 첫 번째 n 개의 인수를 가진 함수를 반환합니다. 예:-
let f x y z = x + y + z
let f1 = f 1
let f2 = f 2
f1 2 3
6
f2 1 3
6
함수를 사용하여 다른 함수를 만드는 방법이 될 수 있습니다.
자바 스크립트에서 :
let add = function(x){
return function(y){
return x + y
};
};
다음과 같이 호출 할 수 있습니까?
let addTen = add(10);
이것이 실행되면 다음 10
과 같이 전달됩니다 x
.
let add = function(10){
return function(y){
return 10 + y
};
};
이것은 우리가이 함수를 반환한다는 것을 의미합니다 :
function(y) { return 10 + y };
그래서 전화하면
addTen();
당신은 정말로 전화하고 있습니다 :
function(y) { return 10 + y };
따라서 이렇게하면 :
addTen(4)
다음과 같습니다.
function(4) { return 10 + 4} // 14
그래서 우리는 addTen()
항상 10을 전달합니다.
let addTwo = add(2) // addTwo(); will add two to whatever you pass in
let addSeventy = add(70) // ... and so on...
이제 분명한 후속 질문은 왜 지구상에서 그렇게하고 싶습니까? 그것은 열심 인 작업 x + y
을 느슨하게 밟을 수있는 작업 으로 바꿉니다 . 즉, 적어도 두 가지 일을 할 수 있습니다 1. 고가의 작업을 캐시합니다. 2. 기능적 패러다임에서 추상화를 얻습니다.
우리의 카레 함수가 다음과 같다고 상상해보십시오.
let doTheHardStuff = function(x) {
let z = doSomethingComputationallyExpensive(x)
return function (y){
z + y
}
}
이 함수를 한 번 호출 한 다음 많은 곳에서 사용하기 위해 결과를 전달할 수 있습니다. 즉, 계산적으로 비싼 작업을 한 번만 수행합니다.
let finishTheJob = doTheHardStuff(10)
finishTheJob(20)
finishTheJob(30)
비슷한 방식으로 추상화를 얻을 수 있습니다.
커리 함수는 첫 번째 인수를 허용하고 두 번째 인수 등을 허용하는 함수를 반환하도록 여러 인수를 다시 작성한 함수입니다. 이를 통해 여러 인수의 함수가 초기 인수 중 일부를 부분적으로 적용 할 수 있습니다.
map
대해 기능 을 수행하려는 경우 수행 할 수 있습니다 . f
xss
map (map f) xss
다음은 Python의 장난감 예입니다.
>>> from functools import partial as curry
>>> # Original function taking three parameters:
>>> def display_quote(who, subject, quote):
print who, 'said regarding', subject + ':'
print '"' + quote + '"'
>>> display_quote("hoohoo", "functional languages",
"I like Erlang, not sure yet about Haskell.")
hoohoo said regarding functional languages:
"I like Erlang, not sure yet about Haskell."
>>> # Let's curry the function to get another that always quotes Alex...
>>> am_quote = curry(display_quote, "Alex Martelli")
>>> am_quote("currying", "As usual, wikipedia has a nice summary...")
Alex Martelli said regarding currying:
"As usual, wikipedia has a nice summary..."
(비 Python 프로그래머의주의를 산만하게하지 않기 위해 +를 통한 연결 만 사용하십시오.)
추가 편집 :
http://docs.python.org/library/functools.html?highlight=partial#functools.partial을 참조 하십시오 . 여기에는 Python이이를 구현하는 방식에서 부분 개체와 기능 구분이 표시됩니다.
카레는 함수를 호출 가능에서 f(a, b, c)
로 호출 가능으로 변환하고 있습니다 f(a)(b)(c)
.
그렇지 않으면 카레는 여러 인수를 인수로 사용하는 일련의 함수로 나누는 함수를 분해 할 때 발생합니다.
말 그대로 카레는 한 가지 방식에서 다른 방식으로 호출하는 기능의 변형입니다. JavaScript에서는 일반적으로 원래 기능을 유지하기 위해 래퍼를 만듭니다.
카레는 함수를 호출하지 않습니다. 그것은 단지 그것을 변형시킵니다.
2 인수 함수에 대해 카레를 수행하는 카레 기능을 만들어 봅시다. 다시 말해, curry(f)
두 개의 인수 f(a, b)
는f(a)(b)
function curry(f) { // curry(f) does the currying transform
return function(a) {
return function(b) {
return f(a, b);
};
};
}
// usage
function sum(a, b) {
return a + b;
}
let carriedSum = curry(sum);
alert( carriedSum(1)(2) ); // 3
보시다시피 구현은 일련의 래퍼입니다.
curry(func)
는 랩퍼 function(a)
입니다.sum(1)
인수가 Lexical Environment에 저장되고 새 랩퍼가 리턴 function(b)
됩니다.sum(1)(2)
마지막으로 function(b)
2를 제공하여 호출하면 원래 다중 인수 합계로 호출을 전달합니다.당신이 이해 partial
하면 반쯤에 있습니다. 아이디어는 partial
함수에 인수를 미리 적용하고 나머지 인수 만 원하는 새 함수를 돌려주는 것입니다. 이 새로운 함수가 호출되면 사전로드 된 인수와 함께 제공된 인수가 포함됩니다.
Clojure에서는 +
기능이지만 완전히 명확하게 만드는 기능입니다.
(defn add [a b] (+ a b))
inc
함수는 전달 된 숫자에 단순히 1을 더한다 는 것을 알고있을 것 입니다.
(inc 7) # => 8
다음을 사용하여 직접 빌드 해 봅시다 partial
.
(def inc (partial add 1))
여기서 첫 번째 인수에 1이로드 된 다른 함수를 반환 add
합니다. add
두 개의 인수를 취하는 것처럼 새 inc
함수는 b
인수 만 원합니다. 1은 이미 부분적으로 적용 되었으므로 이전과 같이 2 개의 인수는 아닙니다 . 따라서 partial
기본값이 사전 제공된 새 기능을 작성하는 도구입니다. 그렇기 때문에 기능적 언어 기능에서 종종 일반에서 특정까지 인수를 주문합니다. 이것은 다른 기능을 구성하기 위해 그러한 기능을 쉽게 재사용 할 수있게합니다.
이제 언어가 내면적으로 이해하기에 충분히 똑똑하여 add
두 가지 주장 을 원한다고 상상해보십시오 . Balking 대신 인수 하나를 전달했을 때 함수가 인수를 부분적으로 적용한 경우 나중에 다른 인수를 제공 할 수 있다는 것을 이해하여 인수를 전달한 경우는 어떻게됩니까? 그런 다음 inc
을 명시 적으로 사용하지 않고 정의 할 수 partial
있습니다.
(def inc (add 1)) #partial is implied
이것은 일부 언어가 작동하는 방식입니다. 함수를 더 큰 변환으로 구성하려고 할 때 매우 유용합니다. 이것은 변환기로 이어질 것입니다.
내가 더 잘 무두질 이해하기 위해,이 기사 및 기사 그것을 참조, 유용한 발견 : http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx
다른 사람들이 언급했듯이 하나의 매개 변수 기능을 갖는 방법 일뿐입니다.
이것은 몇 개의 매개 변수가 전달 될지에 대해 가정 할 필요가 없으므로 2 개의 매개 변수, 3 개의 매개 변수 및 4 개의 매개 변수 기능이 필요하지 않습니다.
다른 모든 답변과 마찬가지로 카레는 부분적으로 적용된 함수를 만드는 데 도움이됩니다. Javascript는 자동 카레를 기본적으로 지원하지 않습니다. 따라서 위에 제공된 예제는 실제 코딩에 도움이되지 않을 수 있습니다. livescript에는 훌륭한 예제가 있습니다 (필수적으로 js로 컴파일하는 것) http://livescript.net/
times = (x, y) --> x * y
times 2, 3 #=> 6 (normal use works as expected)
double = times 2
double 5 #=> 10
위의 예에서 인수를 적게 줄인 경우 livescript는 새로운 카레 함수를 생성합니다 (이중)
카레는 코드를 단순화 할 수 있습니다. 이것이 이것을 사용하는 주된 이유 중 하나입니다. 카레는 n 개의 인수를 허용하는 함수를 하나의 인수 만 허용하는 n 개의 함수로 변환하는 프로세스입니다.
원칙은 클로저 (클로저) 속성을 사용하여 전달 된 함수의 인수를 전달하여 다른 함수에 저장하고이를 반환 값으로 처리하는 것입니다. 이러한 함수는 체인을 형성하고 최종 인수는 전달되어 완성됩니다. 작업.
이것의 장점은 한 번에 하나의 매개 변수를 처리하여 매개 변수 처리를 단순화 할 수 있으며 프로그램의 유연성과 가독성을 향상시킬 수 있다는 것입니다. 또한 프로그램을보다 관리하기 쉽게 만듭니다. 또한 코드를 작은 조각으로 나누면 재사용하기 쉽습니다.
예를 들면 다음과 같습니다.
function curryMinus(x)
{
return function(y)
{
return x - y;
}
}
var minus5 = curryMinus(1);
minus5(3);
minus5(5);
나도 할 수있어 ...
var minus7 = curryMinus(7);
minus7(3);
minus7(5);
복잡한 코드를 깔끔하게 만들고 동기화되지 않은 메소드 등을 처리하는 데 매우 좋습니다.
커리 함수는 하나가 아닌 여러 인수 목록에 적용됩니다.
다음은 두 가지 Int 매개 변수 x와 y를 추가하는 비 커리 링 된 일반 함수입니다.
scala> def plainOldSum(x: Int, y: Int) = x + y
plainOldSum: (x: Int,y: Int)Int
scala> plainOldSum(1, 2)
res4: Int = 3
여기 카레와 비슷한 기능이 있습니다. 두 개의 Int 매개 변수 중 하나의 목록 대신이 함수를 각각 하나의 Int 매개 변수의 두 목록에 적용합니다.
scala> def curriedSum(x: Int)(y: Int) = x + y
curriedSum: (x: Int)(y: Int)Intscala> second(2)
res6: Int = 3
scala> curriedSum(1)(2)
res5: Int = 3
여기서 일어나는 것은 호출 할 때 curriedSum
실제로 두 개의 전통적인 함수 호출을 연속적으로 받는다는 것입니다. 첫 번째 함수 호출은이라는 단일 Int 매개 변수 x
를 사용하고 두 번째 함수의 함수 값을 반환합니다. 이 두 번째 함수는 Int 매개 변수를 사용
y
합니다.
다음 first
은 최초의 전통적인 함수 호출이하는 일을 정신적으로하는 함수입니다 curriedSum
.
scala> def first(x: Int) = (y: Int) => x + y
first: (x: Int)(Int) => Int
첫 번째 함수에 1을 적용하면, 즉 첫 번째 함수를 호출하고 1을 전달하면 두 번째 함수가 생성됩니다.
scala> val second = first(1)
second: (Int) => Int = <function1>
두 번째 함수에 2를 적용하면 결과가 나타납니다.
scala> second(2)
res6: Int = 3
커링의 예는 기능을 사용할 때 현재 매개 변수 중 하나만 알고 있습니다.
예를 들면 다음과 같습니다.
func aFunction(str: String) {
let callback = callback(str) // signature now is `NSData -> ()`
performAsyncRequest(callback)
}
func callback(str: String, data: NSData) {
// Callback code
}
func performAsyncRequest(callback: NSData -> ()) {
// Async code that will call callback with NSData as parameter
}
콜백을 보낼 때 두 번째 매개 변수를 알지 못하므로 performAsyncRequest(_:)
다른 람다 / 클로저를 만들어 함수로 보냅니다.
func callback
자체를 반환? 호출되는 것 @ callback(str)
그래서 let callback = callback(str)
, 콜백은 단지 반환 값이다func callback
func callback(_:data:)
두 개의 매개 변수를 허용합니다. 여기서는 하나만 제공하므로 다음 매개 변수를 String
기다리고 있습니다 ( NSData
). 이제 let callback
데이터가 전달되기를 기다리는 또 다른 함수입니다.
다음은 n no로 함수 카레 링을위한 일반 및 가장 짧은 버전의 예입니다. 매개 변수
const add = a => b => b ? add(a + b) : a;
const add = a => b => b ? add(a + b) : a;
console.log(add(1)(2)(3)(4)());
C #에서 카레 구현에 대한 간단한 설명을 찾을 수 있습니다. 의견에서 나는 카레가 어떻게 유용한 지 보여 주려고 노력했다.
public static class FuncExtensions {
public static Func<T1, Func<T2, TResult>> Curry<T1, T2, TResult>(this Func<T1, T2, TResult> func)
{
return x1 => x2 => func(x1, x2);
}
}
//Usage
var add = new Func<int, int, int>((x, y) => x + y).Curry();
var func = add(1);
//Obtaining the next parameter here, calling later the func with next parameter.
//Or you can prepare some base calculations at the previous step and then
//use the result of those calculations when calling the func multiple times
//with different input parameters.
int result = func(1);
카레는 Java Script의 고차 함수 중 하나입니다.
Currying은 많은 인수의 함수로, 첫 번째 인수를 가져 와서 나머지 인수를 사용하고 값을 반환하는 함수를 반환하도록 다시 작성되었습니다.
혼란 스러운가?
예를 보자.
function add(a,b)
{
return a+b;
}
add(5,6);
이것은 다음과 같은 카레 기능과 유사합니다.
function add(a)
{
return function(b){
return a+b;
}
}
var curryAdd = add(5);
curryAdd(6);
이 코드는 무엇을 의미합니까?
이제 정의를 다시 읽으십시오.
Currying은 많은 인수의 함수로 다시 작성되어 첫 번째 인수를 취하고 나머지 인수를 사용하고 값을 반환하는 함수를 반환합니다.
아직도 혼란스러워? 자세히 설명하겠습니다!
이 함수를 호출하면
var curryAdd = add(5);
다음과 같은 함수를 반환합니다.
curryAdd=function(y){return 5+y;}
이를 고차 함수라고합니다. 즉, 한 함수를 차례로 호출하면 다른 함수가 반환됩니다 (고차 함수의 정확한 정의). 이것은 전설 인 Java Script의 가장 큰 장점입니다. 카레로 돌아와서
이 줄은 두 번째 인수를 curryAdd 함수에 전달합니다.
curryAdd(6);
결과적으로
curryAdd=function(6){return 5+6;}
// Which results in 11
카레 사용법을 이해 하시길 바랍니다. 따라서 장점에 도달하면
왜 카레?
코드 재사용 성을 활용합니다. 적은 코드, 적은 오류. 코드가 적은 방법을 물을 수 있습니까?
ECMA 스크립트 6의 새로운 기능 화살표 기능으로이를 증명할 수 있습니다.
예! ECMA 6, 화살표 기능이라는 멋진 기능을 제공합니다.
function add(a)
{
return function(b){
return a+b;
}
}
화살표 기능을 사용하여 위의 기능을 다음과 같이 작성할 수 있습니다.
x=>y=>x+y
멋지다?
코드가 적고 버그가 적습니다!
이러한 고차 함수의 도움으로 버그가없는 코드를 쉽게 개발할 수 있습니다.
나는 당신에게 도전합니다!
희망, 당신은 카레가 무엇인지 이해했습니다. 설명이 필요하면 여기에 의견을 남겨주세요.
고마워, 좋은 하루 되세요!
"Ceasoning in ReasonML"의 예가 있습니다.
let run = () => {
Js.log("Curryed function: ");
let sum = (x, y) => x + y;
Printf.printf("sum(2, 3) : %d\n", sum(2, 3));
let per2 = sum(2);
Printf.printf("per2(3) : %d\n", per2(3));
};
curry
및uncurry
하스켈 기능한다. 여기서 중요한 것은 이러한 동형이 사전에 고정되어 있으므로 언어에 "내장"되어 있다는 것입니다.