2008-09-22 54 views
157

Tôi không nghĩ rằng mình đã quấy rầy cà ri. Tôi hiểu những gì nó làm, và làm thế nào để làm điều đó. Tôi chỉ không thể nghĩ ra một tình huống tôi sẽ sử dụng nó.Cà ri JavaScript: các ứng dụng thực tế là gì?

Bạn đang sử dụng currying ở đâu (hoặc các thư viện chính sử dụng nó ở đâu)? Thao tác DOM hoặc các ví dụ phát triển ứng dụng chung được chào đón.

One of the answers đề cập đến hoạt ảnh. Các chức năng như slideUp, fadeIn lấy một phần tử làm đối số và thường là hàm được thu thập trả về hàm thứ tự cao với chức năng “hoạt ảnh động” mặc định được tích hợp sẵn. Tại sao điều đó tốt hơn là chỉ áp dụng hàm cao hơn với một số giá trị mặc định?

Có bất kỳ hạn chế nào khi sử dụng không?

Theo yêu cầu đây là một số nguồn lực tốt về currying JavaScript:

tôi sẽ bổ sung thêm khi chúng xuất hiện trong các ý kiến.


Vì vậy, theo câu trả lời, ứng dụng currying và một phần nói chung là các kỹ thuật tiện lợi.

Nếu bạn thường xuyên "tinh chỉnh" chức năng cấp cao bằng cách gọi hàm đó bằng cùng cấu hình, bạn có thể cà ri (hoặc sử dụng phần của Resig) chức năng cấp cao hơn để tạo các phương pháp trợ giúp đơn giản, ngắn gọn.

+0

bạn có thể thêm một liên kết đến một tài nguyên mô tả những gì JS currying là? một bài hướng dẫn hoặc một bài đăng trên blog sẽ tuyệt vời. –

+2

svendtofte.com là longwinded nhưng nếu bạn bỏ qua toàn bộ phần từ "A crash course in ML" và bắt đầu lại tại "Làm thế nào để viết curried JavaScript" nó sẽ trở thành một giới thiệu tuyệt vời để currying trong js. – danio

+1

Đây là điểm khởi đầu tốt để hiểu những gì cà ri và một phần ứng dụng thực sự là: http://slid.es/gsklee/functional-programming-in-5-minutes – Kay

Trả lời

28

@Hank Gay

Đáp lại bình luận EmbiggensTheMind của:

Tôi không thể nghĩ ra một ví dụ nơi currying -by tự-rất hữu ích trong JavaScript; nó là một kỹ thuật để chuyển đổi các cuộc gọi hàm với nhiều đối số thành chuỗi các cuộc gọi hàm với một đối số duy nhất cho mỗi cuộc gọi, nhưng JavaScript hỗ trợ nhiều đối số trong một cuộc gọi hàm duy nhất.

Trong JavaScript — và tôi giả định hầu hết các ngôn ngữ thực tế khác (không phải lambda calculus) —nó thường được liên kết với ứng dụng một phần. John Resig explains it better, nhưng ý chính là có một số logic sẽ được áp dụng cho hai hoặc nhiều đối số, và bạn chỉ biết (các) giá trị cho một số đối số đó.

Bạn có thể sử dụng một phần ứng dụng/currying để sửa các giá trị đã biết và trả về một hàm chỉ chấp nhận các ẩn, được gọi sau khi bạn thực sự có các giá trị bạn muốn chuyển. Điều này cung cấp một cách tiện lợi để tránh lặp lại chính mình khi bạn đã gọi cho cùng một trình xây dựng JavaScript liên tục với tất cả các giá trị giống nhau nhưng một. Để lấy cắp ví dụ của John:

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

Tôi đã cố gắng giải thích nó bằng [một số ví dụ và bản trình diễn trong bài viết của tôi.] (Http://conceptf1.blogspot.com/2014/03/currying-in-javascript.html) –

+4

Đây thực sự là một câu trả lời không tốt. Currying không có gì để làm với một phần ứng dụng. Currying cho phép chế phẩm chức năng. Thành phần chức năng cho phép tái sử dụng chức năng. Sử dụng lại các chức năng làm tăng khả năng bảo trì mã. Nó là dễ dàng! – ftor

1

Đối với thư viện sử dụng, luôn có Functional.

Khi nào nó hữu ích trong JS? Có lẽ cùng một lúc nó rất hữu ích trong các ngôn ngữ hiện đại khác, nhưng thời gian duy nhất tôi có thể nhìn thấy bản thân mình bằng cách sử dụng nó là kết hợp với một phần ứng dụng.

+0

Cảm ơn Hank - bạn có thể mở rộng khi nó hữu ích nói chung không? –

1

Tôi sẽ nói rằng, hầu hết có thể, tất cả thư viện hoạt ảnh trong JS đang sử dụng currying. Thay vì phải chuyển cho mỗi cuộc gọi một tập hợp các yếu tố bị ảnh hưởng và chức năng, mô tả cách thức hoạt động của phần tử, đến chức năng đặt hàng cao hơn sẽ đảm bảo tất cả các công cụ định thời gian, dễ dàng hơn cho khách hàng phát hành. có chức năng như "slideUp", "fadeIn" chỉ lấy các phần tử làm đối số, và đó chỉ là một số hàm curried trả về hàm bậc cao với hàm "hoạt hình" mặc định được tích hợp sẵn.

+0

Tại sao nó tốt hơn để curry các chức năng cao hơn thay vì chỉ đơn giản là gọi nó với một số mặc định? –

+1

Bởi vì nó có nhiều mô-đun hơn để có thể nghiền một "doMathOperation" với phép cộng/phép nhân/vuông/modulus/khác theo mong muốn hơn là tưởng tượng tất cả "mặc định" mà hàm cao hơn có thể hỗ trợ. – gizmo

2

Không có phép thuật hay gì cả ... chỉ là một cách viết tắt dễ chịu cho các chức năng ẩn danh.

một phần (cảnh báo, "FOO!") tương đương với hàm() {alert (" FOO! ");}

một phần (Math.max, 0) tương ứng với hàm (x) {return Math.max (0, x);}

các cuộc gọi đến một phần (MochiKit ngữ. tôi nghĩ rằng một số thư viện khác cung cấp cho các chức năng một phương pháp .curry mà làm điều tương tự) trông hơi đẹp hơn và ít ồn ào hơn so với chức năng ẩn danh.

5

tôi thấy chức năng tương tự như python của hơn functools.partial hữu ích trong JavaScript:

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)); 
    }; 
} 

Tại sao bạn muốn sử dụng? Một tình trạng phổ biến mà bạn muốn sử dụng này là khi bạn muốn để ràng buộc this trong một chức năng để một giá trị:

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

Bây giờ khi gọi lại được gọi, this điểm để obj. Điều này rất hữu ích trong các tình huống sự kiện hoặc để tiết kiệm một số không gian vì nó thường làm cho mã ngắn hơn.

Currying tương tự như một phần với sự khác biệt mà hàm currying trả về chỉ chấp nhận một đối số (theo như tôi hiểu điều đó).

2

Đây là một ví dụ.

Tôi đang thiết kế một loạt các trường bằng JQuery để tôi có thể biết người dùng đang làm gì. Mã này trông như thế này:

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

(Đối với người dùng không JQuery, tôi nói rằng bất cứ lúc nào một vài lĩnh vực có được hay mất tập trung, tôi muốn trackActivity() chức năng được gọi là tôi cũng có thể. sử dụng một hàm ẩn danh, nhưng tôi phải lặp lại nó 4 lần, vì vậy tôi đã lấy nó ra và đặt tên nó.)

Bây giờ hóa ra là một trong các trường đó cần được xử lý khác nhau. Tôi muốn có thể chuyển một tham số vào một trong những cuộc gọi được chuyển đến cơ sở hạ tầng theo dõi của chúng tôi. Với cà ri, tôi có thể.

0

Tôi đồng ý rằng đôi khi bạn muốn nhận được quả bóng lăn bằng cách tạo hàm giả sẽ luôn có giá trị của đối số đầu tiên được điền. May mắn thay, tôi đã xem một thư viện JavaScript hoàn toàn mới có tên là jPaq (h ttp://jpaq.org/) cung cấp chức năng này. Điều tốt nhất về thư viện là bạn có thể tải xuống bản dựng của riêng bạn chỉ chứa mã mà bạn sẽ cần.

1

Chức năng JavaScript được gọi là lamda bằng ngôn ngữ chức năng khác. Nó có thể được sử dụng để soạn một api mới (hàm mạnh hơn hoặc phức tạp hơn) dựa trên đầu vào đơn giản của một nhà phát triển khác. Curry chỉ là một trong những kỹ thuật. Bạn có thể sử dụng nó để tạo ra một api đơn giản để gọi một api phức tạp. Nếu bạn là người phát triển sử dụng api đơn giản (ví dụ bạn sử dụng jQuery để thực hiện thao tác đơn giản), bạn không cần phải sử dụng cà ri. Nhưng nếu bạn muốn tạo api đơn giản, cà ri là bạn của bạn. Bạn phải viết một khung javascript (như jQuery, mootools) hoặc thư viện, sau đó bạn có thể đánh giá cao sức mạnh của nó. Tôi đã viết một chức năng cà ri nâng cao, tại http://blog.semanticsworks.com/2011/03/enhanced-curry-method.html. Bạn không cần đến phương pháp cà ri để làm currying, nó chỉ giúp làm currying, nhưng bạn luôn có thể làm điều đó bằng tay bằng cách viết hàm A() {} để trả về hàm B() {} khác. Để làm cho nó thú vị hơn, sử dụng hàm B() để trả về hàm C() khác.

0

Tôi vừa viết một ví dụ jPaq cho thấy một số ứng dụng tuyệt vời của chức năng cà ri. Kiểm tra nó ra ở đây: Currying Up String Functions

105

Dưới đây là một interesting AND practical use of currying in JavaScript that uses closures:

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" 

này dựa trên một phần mở rộng curry của Function, mặc dù như bạn có thể nhìn thấy nó chỉ sử dụng apply (không có gì quá lạ mắt):

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

Điều này thật tuyệt! Tôi thấy nó tương tự như trích dẫn lisp nói rằng "Lisp là một ngôn ngữ lập trình lập trình" – santiagobasulto

+0

Được rồi ... có lẽ tôi câm ... nhưng tôi không có ý tưởng những gì bạn có ý nghĩa? –

+2

Thú vị, nhưng ví dụ này dường như không hoạt động. 'offset + input' sẽ là' undefined + 1.60936' trong ví dụ 'milesToKm' của bạn; kết quả là 'NaN'. –

0

Chỉ muốn thêm một số tài nguyên cho Functional.js:

Bài giảng/hội nghị giải thích một số ứng dụng http://www.youtube.com/watch?v=HAcN3JyQoyY

Cập nhật Functional.js thư viện: https://github.com/loop-recur/FunctionalJS Một số những người giúp đỡ tốt đẹp (xin lỗi mới ở đây, không có uy tín: p): /loop-recur/PreludeJS

Gần đây tôi đã sử dụng thư viện này để giảm sự lặp lại trong thư viện trợ giúp khách hàng IRC js. Đó là công cụ tuyệt vời - thực sự giúp làm sạch và đơn giản hóa mã.

Ngoài ra, nếu hiệu suất trở thành một vấn đề (nhưng lib này khá nhẹ), thật dễ dàng để viết lại bằng cách sử dụng hàm gốc.

1

Tôi biết chủ đề cũ nhưng tôi sẽ phải thể hiện như thế nào này đang được sử dụng trong các thư viện javascript:

tôi sẽ sử dụng thư viện lodash.js để mô tả những khái niệm cụ thể.

Ví dụ:

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

phần Ứng dụng:

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

currying:

var curriedFn = _.curry(fn); 

Binding:

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

sử dụng:

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! 

khác biệt:

sau khi tách lạng bộ chúng tôi có được một chức năng mới không có tham số trước ràng buộc.

sau khi ứng dụng một phần, chúng tôi nhận được một hàm được ràng buộc với một số tham số trước.

ràng buộc chúng tôi có thể ràng buộc ngữ cảnh sẽ được sử dụng để thay thế ‘this’, nếu không bị ràng buộc mặc định của bất kỳ chức năng nào sẽ là phạm vi cửa sổ.

Lời khuyên: Không cần phải tái tạo lại bánh xe. Một phần ứng dụng/ràng buộc/currying là rất nhiều liên quan. Bạn có thể thấy sự khác biệt ở trên. Sử dụng ý nghĩa này ở bất cứ nơi nào và mọi người sẽ nhận ra những gì bạn đang làm mà không có vấn đề trong sự hiểu biết cộng với bạn sẽ phải sử dụng ít mã hơn.

0

Bạn có thể sử dụng ràng buộc bản địa cho nhanh, một giải pháp dòng

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

2

Đồng ý với Hank Gay - Đó là cực kỳ hữu ích trong các ngôn ngữ lập trình chức năng đúng nhất định - vì đó là một phần cần thiết. Ví dụ, trong Haskell bạn chỉ đơn giản là không thể lấy nhiều tham số cho một hàm - bạn không thể làm điều đó trong lập trình hàm thuần túy. Bạn có một tham số tại một thời điểm và xây dựng chức năng của bạn. Trong JavaScript nó chỉ đơn giản là không cần thiết, mặc dù các ví dụ giả tạo như "chuyển đổi". Dưới đây là mã chuyển đổi tương tự, mà không cần tách lạng bộ:

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 

Tôi xấu muốn Douglas Crockford, trong "JavaScript: The Good Parts", đã đưa ra một số đề cập đến lịch sử và thực tế sử dụng tách lạng bộ chứ không phải là tự nhiên mình nhận xét. Trong thời gian dài nhất sau khi đọc, tôi đã boggled, cho đến khi tôi đang học lập trình chức năng và nhận ra đó là nơi nó đến từ đó.

Sau một số suy nghĩ khác, tôi cho rằng có một trường hợp sử dụng hợp lệ để xử lý trong JavaScript: nếu bạn đang cố gắng viết bằng các kỹ thuật lập trình hàm thuần túy sử dụng JavaScript. Có vẻ như một trường hợp hiếm khi sử dụng.

+1

Mã của bạn dễ hiểu hơn nhiều so với Prisoner Zero và nó giải quyết cùng một vấn đề mà không cần phải làm gì hay phức tạp. Bạn đã có 2 ngón tay cái lên và anh ta có gần 100. Đi con số. – DR01D

+1

Vâng .... https://renomad.com/external_author_docs/parable_two_programmers.txt –

0

Một đâm khác, từ việc làm việc với lời hứa.

(.. Disclaimer: JS Noob, đến từ thế giới Python Ngay cả ở đó, tách lạng bộ không được sử dụng tất cả những gì nhiều, nhưng nó có thể có ích trong dịp Vì vậy, tôi cribbed chức năng currying - xem liên kết)

Trước tiên, tôi bắt đầu bằng cuộc gọi ajax.Tôi có một số quy trình cụ thể để thực hiện thành công, nhưng về thất bại, tôi chỉ muốn cung cấp cho người dùng phản hồi gọi số điều gì đó dẫn đến một số lỗi. Trong mã thực tế của tôi, tôi hiển thị các thông tin phản hồi lỗi trong một bảng điều khiển bootstrap, nhưng tôi chỉ sử dụng đăng nhập ở đây.

Tôi đã sửa đổi url trực tiếp của mình để thực hiện điều này không thành công.

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); 
} 

Bây giờ, ở đây để cho người dùng biết một lô không thành công, vì tất cả mọi thứ đang nhận được là phản hồi từ máy chủ.

Tôi vẫn chỉ có thông tin có sẵn tại thời điểm mã hóa - trong trường hợp của tôi, tôi có một số lô có thể, nhưng tôi không biết số nào đã thất bại. phân tích cú pháp phản hồi của máy chủ về url không thành công.

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

Hãy làm đi. Bảng điều khiển đầu ra là:

console:

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

Bây giờ, chúng ta hãy thay đổi mọi thứ một chút và sử dụng một trình xử lý thất bại chung tái sử dụng, nhưng cũng là một trong đó là cà ri khi chạy với cả tiếng-at ngữ cảnh gọi mã-thời gian và thông tin thời gian chạy có sẵn từ sự kiện.

... 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); 
    }; 
} 

console:

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

Tổng quát hơn, được đưa ra cách sử dụng callback rộng rãi là trong JS, currying có vẻ như một công cụ khá hữu ích để có.

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

Các vấn đề liên quan