JavaScript 카레 : 실용적인 응용 프로그램은 무엇입니까?


172

나는 아직 카레를 먹지 않았다고 생각합니다. 나는 그것이 무엇을하고 어떻게하는지 이해합니다. 나는 그것을 사용할 상황을 생각할 수 없습니다.

JavaScript에서 카레를 사용하는 곳은 어디입니까? DOM 조작 또는 일반적인 응용 프로그램 개발 예제를 환영합니다.

답변 중 하나 는 애니메이션을 언급합니다. 같은 기능은 slideUp, fadeIn인수로 요소를 가지고 일반적으로 내장 된 기본 "애니메이션 기능"과 고차 함수를 반환하는 카레 기능입니다. 일부 기본 설정으로 더 높은 기능을 적용하는 것보다 왜 더 낫습니까?

그것을 사용하는 데 단점이 있습니까?

요청에 따라 JavaScript 커리에 대한 좋은 자료가 있습니다.

의견에서 자라면서 더 추가하겠습니다.


따라서 답변에 따르면 일반적으로 카레 및 부분 적용은 편의 기술입니다.

동일한 구성으로 고수준 함수를 호출하여 자주 "정제"하는 경우, 고수준 함수를 카레 (또는 Resig의 일부 사용)하여 간단하고 간결한 도우미 메서드를 만들 수 있습니다.


JS currying이 무엇인지 설명하는 링크를 추가 할 수 있습니까? 튜토리얼이나 블로그 게시물이 좋습니다.
Eric Schoonover

2
svendtofte.com은 오래 전부터 사용되었지만 "ML의 충돌 과정"에서 전체 섹션을 건너 뛰고 "커리 JavaScript 작성 방법"에서 다시 시작하면 js의 커리에 대한 훌륭한 소개가됩니다.
danio

1
이것은 카레와 부분 적용이 실제로 무엇인지 이해하기에 좋은 출발점입니다. slid.es/gsklee/function-programming-in-5 분
gsklee

1
에 대한 링크 svendtofte.com죽은 외모 -에서 비록 웨이 백 머신에서 그것을 발견 web.archive.org/web/20130616230053/http://www.svendtofte.com/... 죄송합니다, blog.morrisjohns.com/javascript_closures_for_dummies이 내려 것 같다 너무
phatskat

1
BTW, Resig의 부분 버전은 사전 초기화 된 ( "curried") 인수 중 하나에 undefined 값이 지정되면 실패 할 수 있다는 점에서 부족합니다 (확실히 "돈이 아님") . 좋은 카레 링 기능에 관심이있는 사람이라면 올리버 스틸 (Oliver Steele)의 funcitonal.js 에서 원본을 가져와야 합니다.
RobG

답변:


35

@ 행복한 게이

EmbiggensTheMind의 의견에 답변 :

카레 자체만으로도 JavaScript에서 유용한 인스턴스는 생각할 수 없습니다 . 여러 인수를 가진 함수 호출을 각 호출마다 단일 인수를 가진 함수 호출 체인으로 변환하는 기술이지만 JavaScript는 단일 함수 호출에서 여러 인수를 지원합니다.

JavaScript에서는 (람다 미적분학이 아닌) 대부분의 다른 실제 언어를 가정하지만 일반적으로 부분 응용 프로그램과 관련이 있습니다. John Resig 는 더 잘 설명 하지만 요점은 두 개 이상의 인수에 적용될 논리가 있으며 일부 인수에 대한 값만 알고 있다는 것입니다.

부분적 응용 프로그램 / 커링을 사용하여 알려진 값을 수정하고 알 수없는 값만 허용하는 함수를 반환하여 나중에 실제로 전달하려는 값이있을 때 호출 할 수 있습니다. 이렇게하면 동일한 값을 제외한 모든 값을 사용하여 동일한 JavaScript 내장 함수를 반복해서 호출했을 때 자신을 반복하지 않아도됩니다. 요한의 예를 훔치려면 :

String.prototype.csv = String.prototype.split.partial(/,\s*/);
var results = "John, Resig, Boston".csv();
alert( (results[1] == "Resig") + " The text values were split properly" );


6
이것은 실제로 나쁜 대답입니다. 카레는 부분 적용과 관련이 없습니다. 카레는 기능 구성을 가능하게합니다. 기능 구성은 기능 재사용을 가능하게합니다. 기능을 재사용하면 코드 유지 관리 성이 향상됩니다. 그렇게 쉽습니다!

2
@ ftor 선생님, 당신은 매우 나쁜 대답입니다. 카레는 분명히 기능을 더욱 맛있게 만드는 것입니다. 당신은 요점을 분명히 놓쳤다.
Callat

112

클로저를 사용하는 JavaScript에서 흥미롭고 실용적인 커리 사용은 다음과 같습니다 .

function converter(toUnit, factor, offset, input) {
    offset = offset || 0;
    return [((offset + input) * factor).toFixed(2), toUnit].join(" ");
}

var milesToKm = converter.curry('km', 1.60936, undefined);
var poundsToKg = converter.curry('kg', 0.45460, undefined);
var farenheitToCelsius = converter.curry('degrees C', 0.5556, -32);

milesToKm(10);            // returns "16.09 km"
poundsToKg(2.5);          // returns "1.14 kg"
farenheitToCelsius(98);   // returns "36.67 degrees C"

curry확장은 의 확장에 의존 Function하지만 사용하는 것만 볼 수 있습니다 apply(너무 화려 하지는 않습니다).

Function.prototype.curry = function() {
    if (arguments.length < 1) {
        return this; //nothing to curry with - return function
    }
    var __method = this;
    var args = toArray(arguments);
    return function() {
        return __method.apply(this, args.concat([].slice.apply(null, arguments)));
    }
}

5
대단해! "Lisp는 프로그래머블 프로그래밍 언어"라고 말하는 lisp 인용문과 비슷합니다.
산티아고

2
흥미롭지 만이 예제는 효과가없는 것 같습니다. 당신의 모범 offset+input이 될 것입니다 ; 결과 . undefined + 1.60936milesToKmNaN
Nathan Long

3
@
Nathan-

6
내가 지금 읽은 것으로부터, "카레"는 프로토 타입 라이브러리를 사용하거나 직접 추가하지 않는 한, 일반적으로 함수의 트릭 백의 일부가 아닙니다. 그래도 매우 시원합니다.
Roboprog

11
ES5 bind () 메소드에서도 마찬가지입니다. 바인드는 호출 될 때 첫 번째 인수의 컨텍스트와 후속 인수 시퀀스를 사용하여 원래 함수를 호출하는 새 함수를 작성합니다 (앞으로 새 함수에 전달됨). 그래서 당신은 할 수 ... var milesToKm = converter.bind (this, 'km', 1.60936); 또는 var farenheitToCelsius = converter.bind (this, 'C', 0.5556, -32); 첫 번째 인수 인 컨텍스트는 여기와 관련이 없으므로 undefined를 전달할 수 있습니다. 물론 당신이 대체 비 ES5에 대한 자신의 바인드 방법과 기본 기능 프로토 타입을 확대해야
hacklikecrack

7

functools.partialJavaScript 와 유사한 파이썬과 유사한 함수를 발견했습니다 .

function partial(fn) {
  return partialWithScope.apply(this,
    Array.prototype.concat.apply([fn, this],
      Array.prototype.slice.call(arguments, 1)));
}

function partialWithScope(fn, scope) {
  var args = Array.prototype.slice.call(arguments, 2);
  return function() {
    return fn.apply(scope, Array.prototype.concat.apply(args, arguments));
  };
}

왜 그것을 사용하고 싶습니까? 이것을 사용하려는 일반적인 상황 this은 함수를 값 에 바인딩하려는 경우입니다.

var callback = partialWithScope(Object.function, obj);

콜백이 호출되면를 this가리 킵니다 obj. 이것은 일반적으로 코드가 짧아 지므로 이벤트 상황이나 공간을 절약하는 데 유용합니다.

카레는 카레가 반환하는 함수가 하나의 인수를 받아 들인다는 점에서 부분적으로 유사합니다 (내가 이해하는 한).


7

Hank Gay에 동의하기-특정 기능 프로그래밍 언어에 매우 유용합니다. 필요한 부분이기 때문입니다. 예를 들어, Haskell에서는 단순히 함수에 여러 개의 매개 변수를 사용할 수 없습니다. 순수한 함수형 프로그래밍에서는 그렇게 할 수 없습니다. 한 번에 하나의 매개 변수를 사용하여 함수를 작성하십시오. JavaScript에서는 "converter"와 같은 고안된 예제에도 불구하고 단순히 불필요합니다. 카레를 넣지 않아도 동일한 변환기 코드가 있습니다.

var converter = function(ratio, symbol, input) {
    return (input*ratio).toFixed(2) + " " + symbol;
}

var kilosToPoundsRatio = 2.2;
var litersToUKPintsRatio = 1.75;
var litersToUSPintsRatio = 1.98;
var milesToKilometersRatio = 1.62;

converter(kilosToPoundsRatio, "lbs", 4); //8.80 lbs
converter(litersToUKPintsRatio, "imperial pints", 2.4); //4.20 imperial pints
converter(litersToUSPintsRatio, "US pints", 2.4); //4.75 US pints
converter(milesToKilometersRatio, "km", 34); //55.08 km

나는 "자바 스크립트 : 좋은 부분들"에서 Douglas Crockford가 자신의 핸디 한 말보다는 카레의 역사와 실제 사용에 대해 언급했으면 좋겠다. 그것을 읽은 후 가장 오랫동안, 나는 함수형 프로그래밍을 공부하고 그것이 어디에서 왔는지 깨달을 때까지 혼란에 빠졌습니다.

좀 더 생각한 후에 JavaScript에서 카레를 사용하는 유효한 유스 케이스가 하나 있다고 생각합니다. JavaScript를 사용하여 순수한 함수형 프로그래밍 기술을 사용하여 작성하려는 경우. 그러나 드문 유스 케이스처럼 보입니다.


2
코드는 Prisoner Zero 's보다 이해하기 훨씬 쉽고 카레나 복잡한 작업없이 동일한 문제를 해결합니다. 당신은 2 개의 엄지 손가락을 가지고 있고 거의 100을 가지고 있습니다.
DR01D

2

그것은 마법이나 아무것도 아닙니다 ... 익명 함수의 유쾌한 속기입니다.

partial(alert, "FOO!") 에 해당 function(){alert("FOO!");}

partial(Math.max, 0) 에 해당 function(x){return Math.max(0, x);}

부분적인 호출 ( MochiKit 용어. 다른 라이브러리는 함수에 .curry 메소드를 제공하여 동일한 기능을 수행한다고 생각합니다)은 익명 함수보다 약간 멋지고 시끄 럽습니다.


1

그것을 사용하는 라이브러리는 항상 Functional 입니다.

JS에서 언제 유용합니까? 아마도 같은 시간에 다른 현대 언어에서도 유용하지만, 그것을 사용하는 것을 볼 수있는 유일한 시간은 부분 응용 프로그램과 관련이 있습니다.


감사합니다 행크-일반적으로 유용 할 때 확장 할 수 있습니까?
Dave Nolan

1

JS의 모든 애니메이션 라이브러리는 카레를 사용하고 있다고 말할 수 있습니다. 각 호출에 대해 영향을받는 요소 및 요소의 동작 방식을 설명하는 함수 세트를 전달하는 대신 모든 타이밍 요소를 보장하는 고차 함수로 대체하십시오. 일반적으로 고객이 공개 API로 쉽게 릴리스 할 수 있습니다. "slideUp", "fadeIn"과 같은 함수로, 요소 만 인수로 사용하며 기본 "애니메이션 함수"가 내장 된 고차 함수를 반환하는 일부 카레 함수입니다.


단순히 일부 기본값으로 호출하는 대신 highup 기능을 사용하는 것이 더 좋은 이유는 무엇입니까?
Dave Nolan

1
더 높은 기능이 지원할 수있는 모든 "기본"을 상상하는 것보다 원하는대로 더하기 / 곱하기 / 사각형 / 모듈러스 / 기타 calucation과 함께 "doMathOperation"을 카레 할 수있는 것이 훨씬 모듈식이 기 때문입니다.
기즈모

1

다음은 예입니다.

JQuery를 사용하여 여러 필드를 계측하여 사용자가 무엇을하고 있는지 확인할 수 있습니다. 코드는 다음과 같습니다.

$('#foo').focus(trackActivity);
$('#foo').blur(trackActivity);
$('#bar').focus(trackActivity);
$('#bar').blur(trackActivity);

(JQuery가 아닌 사용자의 경우 두 필드가 포커스를 얻거나 잃을 때마다 trackActivity () 함수를 호출하려고합니다. 익명 함수를 사용할 수도 있지만 복제해야합니다. 4 번이나 뽑아서 이름을 붙였습니다.)

이제 이러한 필드 중 하나를 다르게 처리해야합니다. 추적 인프라로 전달되는 해당 호출 중 하나에 매개 변수를 전달하고 싶습니다. 카레와 함께 할 수 있습니다.


1

JavaScript 함수는 다른 기능적 언어에서 lamda라고합니다. 다른 개발자의 간단한 입력을 기반으로 새로운 API (보다 강력하거나 복잡한 기능)를 작성하는 데 사용할 수 있습니다. 카레는 기술 중 하나 일뿐입니다. 이를 사용하여 복잡한 API를 호출하는 단순화 된 API를 만들 수 있습니다. 단순화 된 API를 사용하는 개발자라면 (예를 들어 jQuery를 사용하여 간단한 조작을 수행하는 경우) 카레를 사용할 필요가 없습니다. 그러나 단순화 된 API를 만들고 싶다면 카레가 친구입니다. jQuery, mootools와 같은 자바 스크립트 프레임 워크 또는 라이브러리를 작성해야하며 그 기능을 이해할 수 있습니다. 나는 강화 된 카레 기능을 썼다.http://blog.semanticsworks.com/2011/03/enhanced-curry-method.html. 커리를 수행하기 위해 카레 메소드가 필요하지는 않지만 카레를 수행하는 데 도움이되지만 다른 함수 B () {}를 리턴하기 위해 항상 A () {} 함수를 작성하여 수동으로 수행 할 수 있습니다. 더 흥미롭게하려면 함수 B ()를 사용하여 다른 함수 C ()를 반환하십시오.


1

나는 오래된 스레드를 알고 있지만 이것이 자바 스크립트 라이브러리에서 어떻게 사용되고 있는지 보여 주어야합니다.

lodash.js 라이브러리를 사용하여 이러한 개념을 구체적으로 설명합니다.

예:

var fn = function(a,b,c){ 
return a+b+c+(this.greet || '); 
}

부분 신청 :

var partialFnA = _.partial(fn, 1,3);

카레 :

var curriedFn = _.curry(fn);

제본:

var boundFn = _.bind(fn,object,1,3 );//object= {greet: ’!'}

용법:

curriedFn(1)(3)(5); // gives 9 
or 
curriedFn(1,3)(5); // gives 9 
or 
curriedFn(1)(_,3)(2); //gives 9


partialFnA(5); //gives 9

boundFn(5); //gives 9!

차:

카레 후 우리는 매개 변수가없는 새로운 기능을 얻습니다.

부분 적용 후 우리는 일부 매개 변수가 미리 바인딩 된 함수를 얻습니다.

바인딩에서 우리는 'this'를 대체하는 데 사용될 컨텍스트를 바인딩 할 수 있습니다. 바인딩되지 않은 경우 모든 함수의 기본값은 창 범위입니다.

조언 : 바퀴를 재발 명할 필요가 없습니다. 부분 적용 / 바인딩 / 커링은 매우 관련이 있습니다. 위의 차이점을 볼 수 있습니다. 이 의미를 어디에서나 사용하면 사람들이 이해하는 데 문제없이 수행중인 작업을 인식하고 더 적은 코드를 사용해야합니다.


0

나는 항상 첫 번째 인수의 값을 채울 의사 함수를 만들어서 공을 굴리기를 원한다는 데 동의합니다. 다행히도 jPaq (h ttp : //) 라는 새로운 JavaScript 라이브러리를 발견했습니다 . 이 기능을 제공하는 jpaq.org/ ). 라이브러리의 가장 좋은 점은 필요한 코드 만 포함 된 자체 빌드를 다운로드 할 수 있다는 것입니다.



0

Functional.js에 대한 몇 가지 리소스를 추가하고 싶었습니다.

일부 응용 프로그램을 설명하는 강의 / 회의 http://www.youtube.com/watch?v=HAcN3JyQoyY

Functional.js 라이브러리 업데이트 : https://github.com/loop-recur/FunctionalJS 멋진 조력자 (미안하지만 새로운 평판은 없습니다 : p) : / loop-recur / PreludeJS

js IRC 클라이언트 도우미 라이브러리의 반복을 줄이기 위해 최근 에이 라이브러리를 많이 사용했습니다. 훌륭한 일입니다. 코드를 정리하고 단순화하는 데 실제로 도움이됩니다.

또한 성능이 문제가된다면 (그러나이 lib는 매우 가볍습니다), 기본 함수를 사용하여 쉽게 다시 작성할 수 있습니다.


0

빠른 한 줄 솔루션에 기본 바인드를 사용할 수 있습니다.

function clampAngle(min, max, angle) {
    var result, delta;
    delta = max - min;
    result = (angle - min) % delta;
    if (result < 0) {
        result += delta;
    }
    return min + result;
};

var clamp0To360 = clampAngle.bind(null, 0, 360);

console.log(clamp0To360(405)) // 45


0

약속을 가지고 일하는 것에 대한 또 다른 찌르기.

(면책 조항 : 파이썬 세계에서 온 JS 멍청한 놈. 거기에서도 커링 은 그다지 많이 사용되지는 않지만 가끔 유용 할 수 있습니다.

먼저 아약스 호출로 시작합니다. 성공에 대한 특정 처리가 있지만 실패하면 사용자에게 무언가 를 호출하면 오류가 발생 했다는 피드백을 제공하려고합니다 . 실제 코드에서는 부트 스트랩 패널에 오류 피드백을 표시하지만 여기에서 로깅을 사용하고 있습니다.

이것이 실패하도록 라이브 URL을 수정했습니다.

function ajax_batch(e){
    var url = $(e.target).data("url");

    //induce error
    url = "x" + url;

    var promise_details = $.ajax(
        url,
        {
            headers: { Accept : "application/json" },
            // accepts : "application/json",
            beforeSend: function (request) {
                if (!this.crossDomain) {
                    request.setRequestHeader("X-CSRFToken", csrf_token);
                }
        },
        dataType : "json",
        type : "POST"}
    );
    promise_details.then(notify_batch_success, fail_status_specific_to_batch);
}

이제 사용자에게 배치 실패를 알리려면 오류 처리기에 해당 정보를 작성해야합니다.이 정보는 모두 서버의 응답이기 때문입니다.

나는 여전히 코딩 할 때 정보를 사용할 수 있습니다. 제 경우에는 가능한 많은 배치가 있지만 실패한 URL에 대한 서버 응답을 파싱하는 데 실패한 것을 알 수 없습니다.

function fail_status_specific_to_batch(d){
    console.log("bad batch run, dude");
    console.log("response.status:" + d.status);
}

해보자 콘솔 출력은 다음과 같습니다

콘솔:

bad batch run, dude utility.js (line 109) response.status:404

이제 약간 변경하고 재사용 가능한 일반 실패 처리기를 사용 하고 알려진 코드 타임 호출 컨텍스트와 이벤트에서 사용 가능한 런타임 정보로 런타임에 카레 닝 되는 일반 실패 처리기를 사용하겠습니다 .

    ... rest is as before...
    var target = $(e.target).text();
    var context = {"user_msg": "bad batch run, dude.  you were calling :" + target};
    var contexted_fail_notification = curry(generic_fail, context); 

    promise_details.then(notify_batch_success, contexted_fail_notification);
}

function generic_fail(context, d){
    console.log(context);
    console.log("response.status:" + d.status);
}

function curry(fn) {
     var slice = Array.prototype.slice,
        stored_args = slice.call(arguments, 1);
     return function () {
        var new_args = slice.call(arguments),
              args = stored_args.concat(new_args);
        return fn.apply(null, args);
     };
}

콘솔:

Object { user_msg="bad batch run, dude. you were calling :Run ACL now"} utility.js (line 117) response.status:404 utility.js (line 118)

더 일반적으로 JS에서 콜백 사용법이 널리 퍼져 있다는 점을 감안할 때 카레는 유용한 도구처럼 보입니다.

https://javascriptweblog.wordpress.com/2010/04/05/curry-cooking-up-tastier-functions/ http://www.drdobbs.com/open-source/currying-and-partial-functions-in- javasc / 231001821? pgno = 2

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.