'Curry' là gì?


652

Tôi đã thấy các tài liệu tham khảo về các chức năng bị quấy rối trong một số bài viết và blog nhưng tôi không thể tìm thấy một lời giải thích tốt (hoặc ít nhất là một ý nghĩa có ý nghĩa!)


12
[Còn lại như một nhận xét, vì nó sẽ vô dụng đối với những người không phải là nhà toán học.] Theo định nghĩa của một loại đóng cartesian, có một họ các điều chỉnh cố định (tự nhiên được tham số hóa bởi A) giữa X -> X x A và X -> X ^ A. Các đẳng cấu đồng nhất hom (X x A, Y) <-> hom (X, Y ^ A) là curryvà các uncurryhàm của Haskell. Điều quan trọng ở đây là các đẳng cấu này được cố định trước và do đó "tích hợp" vào ngôn ngữ.
Alexandre C.

3
Có một hướng dẫn thú vị ở đây để làm cà ri trong haskell learnyouahaskell.com/higher-order-fifts#curried-fifts bình luận ngắn gọn là add x y = x+y(được chế biến) khác với add (x, y)=x+y(chưa được xử lý)
Jaider

Câu trả lời:


870

Currying là khi bạn chia nhỏ một hàm lấy nhiều đối số thành một chuỗi các hàm mà mỗi hàm chỉ lấy một đối số. Đây là một ví dụ trong JavaScript:

function add (a, b) {
  return a + b;
}

add(3, 4); // returns 7

Đây là một hàm lấy hai đối số, a và b và trả về tổng của chúng. Bây giờ chúng ta sẽ cà ri chức năng này:

function add (a) {
  return function (b) {
    return a + b;
  }
}

Đây là một hàm lấy một đối số, a và trả về một hàm lấy một đối số khác, b và hàm đó trả về tổng của chúng.

add(3)(4);

var add3 = add(3);

add3(4);

Câu lệnh đầu tiên trả về 7, giống như câu lệnh add (3, 4). Câu lệnh thứ hai định nghĩa một hàm mới gọi là add3 sẽ thêm 3 vào đối số của nó. Đây là những gì một số người có thể gọi là đóng cửa. Câu lệnh thứ ba sử dụng thao tác add3 để thêm 3 đến 4, kết quả lại tạo ra 7.


235
Trong một ý nghĩa thực tế, làm thế nào tôi có thể sử dụng khái niệm này?
Dâu

43
@Strawberry, ví dụ, bạn có một danh sách các số [1, 2, 3, 4, 5]mà bạn muốn nhân với một số tùy ý. Trong Haskell, tôi có thể viết map (* 5) [1, 2, 3, 4, 5]để nhân toàn bộ danh sách 5, và do đó tạo ra danh sách [5, 10, 15, 20, 25].
nyson

62
Tôi hiểu chức năng bản đồ làm gì, nhưng tôi không chắc liệu tôi có hiểu điểm bạn đang cố gắng minh họa cho tôi không. Bạn đang nói chức năng bản đồ đại diện cho khái niệm cà ri?
Dâu

78
@Strawberry Đối số đầu tiên mapphải là một hàm chỉ có 1 đối số - một phần tử từ danh sách. Phép nhân - như một khái niệm toán học - là một phép toán nhị phân; phải mất 2 đối số. Tuy nhiên, trong Haskell *là một hàm curried, tương tự như phiên bản thứ hai addtrong câu trả lời này. Kết quả của (* 5)là một hàm lấy một đối số và nhân nó với 5 và cho phép chúng ta sử dụng nó với bản đồ.
Doval

26
@Strawberry Điều hay ho về các ngôn ngữ chức năng như Standard ML hoặc Haskell là bạn có thể nhận được món cà ri "miễn phí". Bạn có thể định nghĩa một hàm đa đối số như trong bất kỳ ngôn ngữ nào khác và bạn sẽ tự động có được một phiên bản được chế biến của nó, mà không cần phải tự ném vào một bó lambdas. Vì vậy, bạn có thể tạo ra các hàm mới lấy ít đối số từ bất kỳ hàm hiện có nào mà không phải bận tâm hay làm phiền, và điều đó giúp dễ dàng chuyển chúng sang các hàm khác.
Doval

125

Trong một đại số của các hàm, việc xử lý các hàm có nhiều đối số (hoặc một đối số tương đương là một N-tuple) có phần không phù hợp - nhưng, như Moses Schönfinkel (và, độc lập, Haskell Curry) đã chứng minh, không cần thiết: tất cả các bạn cần là các hàm có một đối số.

Vì vậy, làm thế nào để bạn đối phó với một cái gì đó bạn tự nhiên thể hiện như, nói , f(x,y)? Chà, bạn coi nó tương đương với f(x)(y)- f(x), gọi nó glà một hàm và bạn áp dụng hàm đó cho y. Nói cách khác, bạn chỉ có các hàm lấy một đối số - nhưng một số hàm đó trả về các hàm khác (mà CSONG lấy một đối số ;-).

Như thường lệ, wikipedia có một mục tóm tắt hay về điều này, với nhiều gợi ý hữu ích (có thể bao gồm những gợi ý liên quan đến ngôn ngữ yêu thích của bạn ;-) cũng như xử lý toán học khắt khe hơn một chút.


1
Tôi cho rằng nhận xét tương tự như tôi ở trên - Tôi chưa thấy các ngôn ngữ chức năng hạn chế các chức năng để lấy một đối số. Tôi có nhầm không?
Eric M

1
@hoohoo: Các ngôn ngữ chức năng thường không giới hạn các hàm trong một đối số duy nhất. Tuy nhiên, ở cấp độ toán học thấp hơn, dễ dàng hơn rất nhiều để xử lý các hàm chỉ lấy một đối số. (Ví dụ, trong phép tính lambda, các hàm chỉ nhận một đối số tại một thời điểm.)
Sam DeFabbia-Kane

1
ĐỒNG Ý. Một câu hỏi khác sau đó. Đây có phải là một tuyên bố đúng? Tính toán Lambda có thể được sử dụng như một mô hình lập trình chức năng nhưng lập trình chức năng không nhất thiết phải áp dụng tính toán lambda.
Eric M

7
Như các trang wikipedia lưu ý, hầu hết các ngôn ngữ FP "tô điểm" hoặc "gia tăng" phép tính lambda (ví dụ với một số hằng số và kiểu dữ liệu) thay vì chỉ "áp dụng" nó, nhưng nó không gần lắm. BTW, điều gì mang lại cho bạn ấn tượng, ví dụ như Haskell KHÔNG "hạn chế các chức năng để lấy một đối số"? Nó chắc chắn, mặc dù điều đó không liên quan nhờ vào cà ri; ví dụ div :: Integral a => a -> a -> a- lưu ý nhiều mũi tên? "Ánh xạ a đến chức năng ánh xạ a đến a" là một lần đọc ;-). Bạn có thể sử dụng một đối số tuple (đơn) cho div& c, nhưng điều đó thực sự chống lại thành ngữ trong Haskell.
Alex Martelli

@Alex - wrt Haskell & arg Count, tôi đã không dành nhiều thời gian cho Haskell, và đó là tất cả vài tuần trước. Vì vậy, đó là một lỗi dễ dàng để thực hiện.
Eric M

99

Đây là một ví dụ cụ thể:

Giả sử bạn có một hàm tính toán lực hấp dẫn tác dụng lên một vật. Nếu bạn không biết công thức, bạn có thể tìm thấy nó ở đây . Hàm này có ba tham số cần thiết làm đối số.

Bây giờ, đang ở trên trái đất, bạn chỉ muốn tính toán lực cho các vật thể trên hành tinh này. Trong một ngôn ngữ chức năng, bạn có thể chuyển khối lượng của trái đất đến chức năng và sau đó đánh giá một phần nó. Những gì bạn nhận được là một chức năng khác chỉ cần hai đối số và tính toán lực hấp dẫn của các vật thể trên trái đất. Điều này được gọi là cà ri.


2
Như một sự tò mò, thư viện Prototype cho JavaScript cung cấp một "cà ri" chức năng nào khá nhiều chính xác những gì bạn đã giải thích ở đây: prototypejs.org/api/function/curry
shuckster

Liên kết chức năng cà ri PrototypeJS mới. otypejs.org/doc/latest/lingu/Function/prototype/curry/iêu
Richard Ayotte

7
Điều này nghe có vẻ như một phần ứng dụng với tôi. Hiểu biết của tôi là nếu bạn áp dụng currying, bạn có thể tạo các hàm với một đối số duy nhất và kết hợp chúng để tạo thành các hàm phức tạp hơn. Tui bỏ lỡ điều gì vậy?
neontapir

9
@neontapir là chính xác. Những gì Shea mô tả không phải là cà ri. Nó là một phần ứng dụng. Nếu một hàm ba đối số được xử lý và bạn gọi nó là f (1), thì cái bạn nhận lại không phải là hàm hai đối số. Bạn nhận lại một hàm một đối số trả về một hàm một đối số khác. Một hàm curried chỉ có thể được thông qua một đối số. Chức năng cà ri trong PrototypeJS cũng không phải là cà ri. Đó là một phần ứng dụng.
MindJuice

không (để đánh giá một phần) và không (để cà ri). điều này được gọi là một phần ứng dụng. cà ri là cần thiết để kích hoạt nó.
Will Ness

47

Currying là một phép biến đổi có thể được áp dụng cho các hàm để cho phép chúng lấy một đối số ít hơn so với trước đây.

Ví dụ: trong F #, bạn có thể xác định hàm do đó: -

let f x y z = x + y + z

Ở đây hàm f lấy tham số x, y và z và tính tổng chúng với nhau để: -

f 1 2 3

Trả về 6.

Do đó, từ định nghĩa của chúng tôi, chúng tôi có thể định nghĩa hàm cà ri cho f: -

let curry f = fun x -> f x

Trong đó 'fun x -> fx' là hàm lambda tương đương với x => f (x) trong C #. Hàm này nhập vào hàm bạn muốn cà ri và trả về một hàm có một đối số duy nhất và trả về hàm đã chỉ định với đối số đầu tiên được đặt thành đối số đầu vào.

Sử dụng ví dụ trước của chúng tôi, chúng tôi có thể có được một cà ri f do đó: -

let curryf = curry f

Sau đó chúng ta có thể làm như sau: -

let f1 = curryf 1

Cung cấp cho chúng ta một hàm F1 tương đương với F1 yz = 1 + y + z. Điều này có nghĩa là chúng ta có thể làm như sau: -

f1 2 3

Mà trả về 6.

Quá trình này thường bị nhầm lẫn với 'ứng dụng chức năng một phần' có thể được định nghĩa như sau: -

let papply f x = f x

Mặc dù chúng ta có thể mở rộng nó thành nhiều hơn một tham số, tức là: -

let papply2 f x y = f x y
let papply3 f x y z = f x y z
etc.

Một ứng dụng một phần sẽ lấy hàm và tham số (s) và trả về một hàm yêu cầu một hoặc nhiều tham số, và như hai ví dụ trước hiển thị được thực hiện trực tiếp trong định nghĩa hàm F # tiêu chuẩn để chúng ta có thể đạt được kết quả trước đó: -

let f1 = f 1
f1 2 3

Mà sẽ trả về kết quả là 6.

Tóm lại là:-

Sự khác biệt giữa ứng dụng currying và một phần chức năng là: -

Currying nhận một hàm và cung cấp một hàm mới chấp nhận một đối số duy nhất và trả về hàm đã chỉ định với đối số đầu tiên được đặt cho đối số đó. Điều này cho phép chúng ta biểu diễn các hàm với nhiều tham số dưới dạng một loạt các hàm đối số duy nhất . Thí dụ:-

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

Ứng dụng hàm một phần trực tiếp hơn - nó nhận một hàm và một hoặc nhiều đối số và trả về một hàm với n đối số đầu tiên được đặt thành n đối số được chỉ định. Thí dụ:-

let f x y z = x + y + z
let f1 = f 1
let f2 = f 2
f1 2 3
6
f2 1 3
6

Vì vậy, các phương thức trong C # sẽ cần phải được xử lý trước khi chúng có thể được áp dụng một phần?
cdmckay

"Điều này cho phép chúng tôi biểu diễn các hàm với nhiều tham số dưới dạng một loạt các hàm đối số duy nhất" - hoàn hảo, điều đó đã xóa sạch tất cả đối với tôi. Cảm ơn
Phân tích mờ

44

Nó có thể là một cách để sử dụng các chức năng để thực hiện các chức năng khác.

Trong javascript:

let add = function(x){
  return function(y){ 
   return x + y
  };
};

Sẽ cho phép chúng tôi gọi nó như vậy:

let addTen = add(10);

Khi điều này chạy 10được thông qua như x;

let add = function(10){
  return function(y){
    return 10 + y 
  };
};

có nghĩa là chúng ta được trả về chức năng này:

function(y) { return 10 + y };

Vì vậy, khi bạn gọi

 addTen();

bạn đang thực sự gọi:

 function(y) { return 10 + y };

Vì vậy, nếu bạn làm điều này:

 addTen(4)

nó giống như:

function(4) { return 10 + 4} // 14

Vì vậy, chúng tôi addTen()luôn thêm mười vào bất cứ thứ gì chúng tôi vượt qua. Chúng tôi có thể thực hiện các chức năng tương tự theo cùng một cách:

let addTwo = add(2)       // addTwo(); will add two to whatever you pass in
let addSeventy = add(70)  // ... and so on...

Bây giờ câu hỏi tiếp theo rõ ràng là tại sao bạn muốn làm điều đó trên trái đất? Nó biến những gì là một hoạt động háo hức x + ythành một hoạt động có thể được thực hiện một cách lười biếng, có nghĩa là chúng ta có thể thực hiện ít nhất hai điều 1. lưu trữ các hoạt động đắt tiền 2. đạt được sự trừu tượng trong mô hình chức năng.

Hãy tưởng tượng chức năng curried của chúng tôi trông như thế này:

let doTheHardStuff = function(x) {
  let z = doSomethingComputationallyExpensive(x)
  return function (y){
    z + y
  }
}

Chúng ta có thể gọi hàm này một lần, sau đó chuyển qua kết quả sẽ được sử dụng ở nhiều nơi, nghĩa là chúng ta chỉ thực hiện các công cụ tính toán đắt tiền một lần:

let finishTheJob = doTheHardStuff(10)
finishTheJob(20)
finishTheJob(30)

Chúng ta có thể có được sự trừu tượng theo cách tương tự.


5
Giải thích từng bước tốt nhất về một quá trình tuần tự vốn có mà tôi đã thấy ở đây, và có lẽ là câu trả lời hay nhất, lý giải nhất trong số rất nhiều.

4
@jonsilver Tôi sẽ nói ngược lại, không phải là một lời giải thích tốt. Tôi đồng ý rằng thật tốt khi giải thích ví dụ được đặt ra, nhưng mọi người có xu hướng mặc định suy nghĩ, thì vâng hoàn toàn rõ ràng nhưng tôi có thể làm điều tương tự theo cách khác vậy cà ri có gì hay? Nói cách khác, tôi ước nó có đủ bối cảnh hoặc lời giải thích để chiếu sáng không chỉ cách thức hoạt động của cà ri, mà còn tại sao nó không phải là một quan sát vô ích và tầm thường so với các cách thêm mười.
whitneyland

29

Hàm curried là một hàm của một số đối số được viết lại sao cho nó chấp nhận đối số thứ nhất và trả về một hàm chấp nhận đối số thứ hai, v.v. Điều này cho phép các hàm của một số đối số có một số đối số ban đầu được áp dụng một phần.


5
"Điều này cho phép các hàm của một số đối số có một số đối số ban đầu được áp dụng một phần." - tại sao điều đó có lợi?
acarlon

5
Các hàm @acarlon thường được gọi lặp đi lặp lại với một hoặc nhiều đối số giống nhau. Ví dụ, nếu bạn muốn mapmột chức năng ftrong danh sách các danh sách xssbạn có thể làm map (map f) xss.
Jon Harrop

1
Cảm ơn bạn, điều đó có ý nghĩa. Tôi đã đọc thêm một chút và nó đã rơi vào vị trí.
acarlon

4
Tôi nghĩ rằng câu trả lời này làm cho nó đúng theo một cách súc tích tốt đẹp. "Currying" là quá trình lấy chức năng của nhiều đối số và chuyển đổi nó thành một hàm nghiêm trọng, mỗi hàm lấy một đối số duy nhất và trả về một hàm của một đối số hoặc trong trường hợp của hàm cuối cùng, trả về kết quả thực tế . Điều này có thể được thực hiện tự động cho bạn bằng ngôn ngữ hoặc bạn có thể gọi hàm curry () bằng các ngôn ngữ khác để tạo phiên bản curried. Lưu ý rằng việc gọi một hàm curried với một tham số không phải là currying. Việc cà ri đã xảy ra.
MindJuice

7

Đây là một ví dụ đồ chơi trong 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..."

(Chỉ sử dụng phép nối thông qua + để tránh phân tâm cho các lập trình viên không phải là Python.)

Chỉnh sửa để thêm:

Xem http://docs.python.org/l Library / funcools.html? Highlight = part # funcools.partial , cũng hiển thị đối tượng một phần so với phân biệt chức năng theo cách Python thực hiện điều này.


Tôi không hiểu điều này - bạn làm điều này: >>> am_quote = curry (display_quote, "Alex Martelli") nhưng sau đó bạn làm điều này tiếp theo: >>> am_quote ("currying", "Như thường lệ, wikipedia có một bản tóm tắt hay. .. ") Vì vậy, bạn có một chức năng với hai đối số. Có vẻ như cà ri sẽ cung cấp cho bạn ba func khác nhau mà bạn sẽ sáng tác?
Eric M

Tôi đang sử dụng một phần để cà ri chỉ một tham số, tạo ra một hàm có hai đối số. Nếu bạn muốn, bạn có thể tiếp tục cà ri am_quote để tạo một cái chỉ trích dẫn Alex về một chủ đề cụ thể. Backgound toán học có thể được tập trung vào việc kết thúc với các hàm chỉ có một tham số - nhưng tôi tin rằng việc sửa bất kỳ số lượng tham số nào như thế này là phổ biến (nếu không chính xác theo quan điểm toán học) được gọi là currying.
Anon

(btw - '>>>' là lời nhắc trong trình thông dịch tương tác Python, không phải là một phần của mã.)
Anon

OK cảm ơn đã làm rõ về args. Tôi biết về lời nhắc của trình thông dịch Python, tôi đã cố gắng trích dẫn các dòng nhưng nó không hoạt động ;-)
Eric M

Sau khi nhận xét của bạn, tôi đã tìm kiếm và tìm thấy các tài liệu tham khảo khác, bao gồm cả ở đây trên SO, về sự khác biệt giữa "currying" và. "ứng dụng một phần" để đáp ứng với nhiều trường hợp sử dụng không chính xác mà tôi quen thuộc. Xem ví dụ: stackoverflow.com/questions/218025/ Kẻ
Anon

5

Currying đang dịch một chức năng từ có thể gọi f(a, b, c)thành thành có thể gọi là f(a)(b)(c).

Mặt khác, currying là khi bạn chia nhỏ một hàm đưa nhiều đối số thành một chuỗi các hàm tham gia vào các đối số.

Theo nghĩa đen, currying là một sự chuyển đổi của các chức năng: từ cách gọi này sang cách khác. Trong JavaScript, chúng ta thường tạo một trình bao bọc để giữ chức năng ban đầu.

Currying không gọi một chức năng. Nó chỉ biến đổi nó.

Hãy tạo hàm cà ri thực hiện chức năng cà ri cho các hàm hai đối số. Nói cách khác, curry(f)đối với hai đối số f(a, b)dịch nó thànhf(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

Như bạn có thể thấy, việc thực hiện là một loạt các hàm bao.

  • Kết quả curry(func)là một bọc function(a).
  • Khi nó được gọi như thế sum(1), đối số được lưu trong Môi trường từ điển và một trình bao bọc mới được trả về function(b).
  • Sau đó, sum(1)(2)cuối cùng các cuộc gọi function(b)cung cấp 2 và nó chuyển cuộc gọi đến tổng đa đối số ban đầu.

4

Nếu bạn hiểu partialbạn đang ở giữa chừng. Ý tưởng partiallà để áp dụng lại các đối số cho một hàm và trả lại một hàm mới chỉ muốn các đối số còn lại. Khi hàm mới này được gọi, nó bao gồm các đối số được tải sẵn cùng với bất kỳ đối số nào được cung cấp cho nó.

Trong Clojure +là một chức năng nhưng để làm cho mọi thứ rõ ràng rõ ràng:

(defn add [a b] (+ a b))

Bạn có thể nhận thấy rằng inchàm chỉ cần thêm 1 vào bất kỳ số nào nó được chuyển.

(inc 7) # => 8

Hãy tự xây dựng nó bằng cách sử dụng partial:

(def inc (partial add 1))

Ở đây chúng ta trả về một hàm khác có 1 được nạp vào đối số đầu tiên của add. Khi addcó hai đối số, inchàm mới chỉ muốn bđối số - không phải là 2 đối số như trước vì 1 đã được áp dụng một phần . Do đó, partiallà một công cụ để tạo các hàm mới với các giá trị mặc định được đặt trước. Đó là lý do tại sao trong một chức năng ngôn ngữ chức năng thường sắp xếp các đối số từ chung đến cụ thể. Điều này giúp dễ dàng sử dụng lại các chức năng như vậy để xây dựng các chức năng khác.

Bây giờ hãy tưởng tượng nếu ngôn ngữ đủ thông minh để hiểu nội tâm mà addmuốn hai đối số. Khi chúng ta truyền cho nó một đối số, thay vì chùn bước, điều gì sẽ xảy ra nếu hàm áp dụng một phần đối số mà chúng ta đã thay mặt nó hiểu rằng chúng ta có thể có nghĩa là cung cấp đối số khác sau này? Sau đó chúng ta có thể định nghĩa incmà không sử dụng rõ ràng partial.

(def inc (add 1)) #partial is implied

Đây là cách một số ngôn ngữ ứng xử. Nó đặc biệt hữu ích khi người ta muốn kết hợp các hàm thành các phép biến đổi lớn hơn. Điều này sẽ dẫn một đến đầu dò.


3

Tôi tìm thấy bài viết này, và bài viết mà nó tham khảo, hữu ích, để hiểu rõ hơn về cà ri: http://bloss.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-feft-application.aspx

Như những người khác đã đề cập, nó chỉ là một cách để có một hàm tham số.

Điều này hữu ích ở chỗ bạn không phải giả sử có bao nhiêu tham số sẽ được truyền vào, vì vậy bạn không cần 2 tham số, 3 tham số và 4 hàm tham số.


3

Như tất cả các câu trả lời khác, currying giúp tạo ra các chức năng được áp dụng một phần. Javascript không cung cấp hỗ trợ riêng cho cà ri tự động. Vì vậy, các ví dụ được cung cấp ở trên có thể không giúp ích trong việc mã hóa thực tế. Có một số ví dụ tuyệt vời trong lifcript (mà về cơ bản biên dịch thành js) http://livescript.net/

times = (x, y) --> x * y
times 2, 3       #=> 6 (normal use works as expected)
double = times 2
double 5         #=> 10

Trong ví dụ trên, khi bạn đã đưa ra ít hơn, không có đối số, bản mô tả sẽ tạo ra hàm curried mới cho bạn (gấp đôi)


3

Curry có thể đơn giản hóa mã của bạn. Đây là một trong những lý do chính để sử dụng này. Currying là một quá trình chuyển đổi một hàm chấp nhận n đối số thành n hàm chỉ chấp nhận một đối số.

Nguyên tắc là truyền các đối số của hàm đã truyền, sử dụng thuộc tính đóng (đóng), để lưu trữ chúng trong hàm khác và coi nó như một giá trị trả về, và các hàm này tạo thành một chuỗi và các đối số cuối cùng được truyền vào để hoàn thành các hoạt động.

Lợi ích của việc này là nó có thể đơn giản hóa việc xử lý các tham số bằng cách xử lý một tham số tại một thời điểm, điều này cũng có thể cải thiện tính linh hoạt và dễ đọc của chương trình. Điều này cũng làm cho chương trình dễ quản lý hơn. Ngoài ra, việc chia mã thành các phần nhỏ hơn sẽ giúp nó tái sử dụng.

Ví dụ:

function curryMinus(x) 
{
  return function(y) 
  {
    return x - y;
  }
}

var minus5 = curryMinus(1);
minus5(3);
minus5(5);

Tôi cũng có thể làm ...

var minus7 = curryMinus(7);
minus7(3);
minus7(5);

Điều này rất tốt để làm cho mã phức tạp gọn gàng và xử lý các phương thức không đồng bộ, v.v.


2

Hàm curried được áp dụng cho nhiều danh sách đối số, thay vì chỉ một.

Đây là một hàm thông thường, không bị cong, có thêm hai tham số Int, x và y:

scala> def plainOldSum(x: Int, y: Int) = x + y
plainOldSum: (x: Int,y: Int)Int
scala> plainOldSum(1, 2)
res4: Int = 3

Đây là chức năng tương tự mà bị quấy rầy. Thay vì một danh sách gồm hai tham số Int, bạn áp dụng chức năng này cho hai danh sách của một tham số Int mỗi:

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

Điều xảy ra ở đây là khi bạn gọi curriedSum, bạn thực sự nhận được hai cách gọi hàm truyền thống trở lại. Lệnh gọi hàm đầu tiên nhận một tham số Int duy nhất có tên xvà trả về giá trị hàm cho hàm thứ hai. Hàm thứ hai này lấy tham số Int y.

Đây là một chức năng được đặt tên theo firsttinh thần mà việc gọi hàm truyền thống đầu tiên curriedSumsẽ làm:

scala> def first(x: Int) = (y: Int) => x + y
first: (x: Int)(Int) => Int

Áp dụng 1 cho hàm thứ nhất, nói cách khác, gọi hàm thứ nhất và truyền vào 1 1yyield hàm thứ hai:

scala> val second = first(1)
second: (Int) => Int = <function1>

Áp dụng 2 cho hàm thứ hai mang lại kết quả:

scala> second(2)
res6: Int = 3

2

Một ví dụ về currying sẽ là khi có các chức năng bạn chỉ biết một trong các tham số tại thời điểm này:

Ví dụ:

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
}

Ở đây, vì bạn không biết tham số thứ hai cho cuộc gọi lại khi gửi nó cho performAsyncRequest(_:)bạn, bạn sẽ phải tạo một lambda / bao đóng khác để gửi tham số đó đến hàm.


func callbacktrở về chính nó? Nó được gọi là @ callback(str)vì vậy let callback = callback(str), gọi lại chỉ là giá trị trả về củafunc callback
nikk wong

không, func callback(_:data:)chấp nhận hai tham số, ở đây tôi chỉ cung cấp cho nó một tham số String, vì vậy nó đang chờ thông số tiếp theo ( NSData), đây là lý do tại sao bây giờ let callbacklà một hàm khác đang chờ dữ liệu được truyền vào
S2dent

2

Dưới đây là ví dụ về phiên bản chung và phiên bản ngắn nhất để thực hiện chức năng curry với n no. của thông số

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


1

Ở đây bạn có thể tìm thấy một lời giải thích đơn giản về việc thực hiện currying trong C #. Trong các bình luận, tôi đã cố gắng chỉ ra cách làm cà ri có thể hữu ích:

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

1

Currying là một trong những hàm bậc cao hơn của Java Script.

Currying là một hàm của nhiều đối số được viết lại sao cho nó nhận đối số đầu tiên và trả về một hàm lần lượt sử dụng các đối số còn lại và trả về giá trị.

Bối rối?

Hãy xem một ví dụ,

function add(a,b)
    {
        return a+b;
    }
add(5,6);

Điều này tương tự như chức năng cà ri sau đây,

function add(a)
    {
        return function(b){
            return a+b;
        }
    }
var curryAdd = add(5);
curryAdd(6);

Vậy mã này có nghĩa là gì?

Bây giờ đọc lại định nghĩa,

Currying là một hàm của nhiều đối số được viết lại sao cho nó nhận đối số đầu tiên và trả về một hàm lần lượt sử dụng các đối số còn lại và trả về giá trị.

Vẫn còn bối rối? Hãy để tôi giải thích sâu sắc!

Khi bạn gọi chức năng này,

var curryAdd = add(5);

Nó sẽ trả về cho bạn một chức năng như thế này,

curryAdd=function(y){return 5+y;}

Vì vậy, đây được gọi là chức năng bậc cao. Có nghĩa là, lần lượt gọi một hàm trả về một hàm khác là một định nghĩa chính xác cho hàm bậc cao hơn. Đây là lợi thế lớn nhất cho huyền thoại, Java Script. Vì vậy, trở lại với cà ri,

Dòng này sẽ truyền đối số thứ hai cho hàm curryAdd.

curryAdd(6);

lần lượt kết quả,

curryAdd=function(6){return 5+6;}
// Which results in 11

Hy vọng bạn hiểu cách sử dụng cà ri ở đây. Vì vậy, đến với những lợi thế,

Tại sao lại là cà ri?

Nó sử dụng khả năng sử dụng lại mã. Ít mã hơn, ít lỗi hơn. Bạn có thể hỏi làm thế nào nó là ít mã?

Tôi có thể chứng minh điều đó với ECMA script 6 chức năng mũi tên tính năng mới.

Đúng! ECMA 6, cung cấp cho chúng tôi tính năng tuyệt vời được gọi là chức năng mũi tên,

function add(a)
    {
        return function(b){
            return a+b;
        }
    }

Với sự trợ giúp của hàm mũi tên, chúng ta có thể viết hàm trên như sau,

x=>y=>x+y

Thật tuyệt phải không?

Vì vậy, ít mã hơn và ít lỗi hơn !!

Với sự trợ giúp của các hàm bậc cao hơn này, người ta có thể dễ dàng phát triển mã không có lỗi.

Tôi thách thức bạn!

Hy vọng, bạn đã hiểu thế nào là cà ri. Xin vui lòng bình luận ở đây nếu bạn cần bất kỳ làm rõ.

Cảm ơn chúc một ngày tốt lành!


0

Có một ví dụ về "Currying 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));
  };
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.