Sự khác biệt giữa cà ri và ứng dụng một phần là gì?


438

Tôi thường thấy trên Internet rất nhiều khiếu nại rằng các ví dụ khác về cà ri không phải là cà ri, nhưng thực ra chỉ là một phần ứng dụng.

Tôi đã không tìm thấy một lời giải thích hợp lý về ứng dụng một phần là gì, hoặc nó khác với cà ri như thế nào. Dường như có một sự nhầm lẫn chung, với các ví dụ tương đương được mô tả là cà ri ở một số nơi và ứng dụng một phần ở những nơi khác.

Ai đó có thể cung cấp cho tôi một định nghĩa về cả hai thuật ngữ và chi tiết về cách chúng khác nhau không?

Câu trả lời:


256

Currying đang chuyển đổi một hàm duy nhất của n đối số thành n hàm với mỗi đối số duy nhất. Cho hàm sau:

function f(x,y,z) { z(x(y));}

Khi bị quấy rầy, trở thành:

function f(x) { lambda(y) { lambda(z) { z(x(y)); } } }

Để có được ứng dụng đầy đủ của f (x, y, z), bạn cần làm điều này:

f(x)(y)(z);

Nhiều ngôn ngữ chức năng cho phép bạn viết f x y z. Nếu bạn chỉ gọi f x yhoặc f (x) (y) thì bạn nhận được hàm được áp dụng một phần, giá trị trả về là một giá trị đóng lambda(z){z(x(y))}với các giá trị của x và y được truyền vào f(x,y).

Một cách để sử dụng ứng dụng một phần là xác định hàm là ứng dụng một phần của hàm tổng quát, như nếp gấp :

function fold(combineFunction, accumulator, list) {/* ... */}
function sum     = curry(fold)(lambda(accum,e){e+accum}))(0);
function length  = curry(fold)(lambda(accum,_){1+accum})(empty-list);
function reverse = curry(fold)(lambda(accum,e){concat(e,accum)})(empty-list);

/* ... */
@list = [1, 2, 3, 4]
sum(list) //returns 10
@f = fold(lambda(accum,e){e+accum}) //f = lambda(accumulator,list) {/*...*/}
f(0,list) //returns 10
@g = f(0) //same as sum
g(list)  //returns 10

40
Bạn đang nói rằng ứng dụng một phần là khi bạn cà ri một hàm và sử dụng một số, nhưng không phải tất cả các hàm kết quả?
SpoonMeiser

9
nhiều hơn hoặc ít hơn Nếu bạn chỉ cung cấp một tập hợp con của các đối số, bạn sẽ nhận lại một hàm chấp nhận phần còn lại của các đối số
Đánh dấu Cidade

1
Việc thay đổi một hàm f (a, b, c, d) thành g (a, b) được tính là một phần ứng dụng? Hoặc chỉ khi áp dụng cho các chức năng curried? Xin lỗi để trở thành một nỗi đau, nhưng tôi đang tức giận cho một câu trả lời rõ ràng ở đây.
SpoonMeiser

2
@Mark: Tôi đoán đây chỉ là một trong những khái niệm mang lại cho người bán hàng trong tôi - nhưng một sự hấp dẫn đối với các nguồn có thẩm quyền không làm thỏa mãn, vì tất cả chúng dường như chỉ ra cho nhau. Wikipedia hầu như không phải là những gì tôi coi là một nguồn có thẩm quyền, nhưng tôi hiểu rằng thật khó để tìm thấy nhiều thứ khác. Đủ để nói rằng tôi nghĩ cả hai chúng ta đều biết rằng chúng ta nói và sức mạnh của chúng, bất kể chúng ta có thể đồng ý (hoặc không đồng ý) về các chi tiết của tiếng bản địa hay không! :) Cảm ơn Mark!
Jason Bunting

5
@JasonBunting, Liên quan đến bình luận đầu tiên của bạn, những gì bạn đang nói về đang giải mã . Currying đang lấy một hàm multi-arg làm đầu vào và trả về một chuỗi các hàm 1-arg làm đầu ra. De-currying đang lấy một chuỗi các hàm 1-arg làm đầu vào và trả về một hàm nhiều arg làm đầu ra. Như được xây dựng trên stackoverflow.com/a/23438430/632951
Pacerier

165

Cách dễ nhất để xem chúng khác nhau như thế nào là xem xét một ví dụ thực tế . Giả sử rằng chúng ta có một hàm Addlấy 2 số làm đầu vào và trả về một số làm đầu ra, ví dụ Add(7, 5)trả về 12. Trong trường hợp này:

  • Áp dụng một phần hàm Addvới một giá trị 7sẽ cho chúng ta một hàm mới làm đầu ra. Hàm đó tự lấy 1 số làm đầu vào và xuất ra một số. Như vậy:

    Partial(Add, 7); // returns a function f2 as output
    
                     // f2 takes 1 number as input and returns a number as output
    

    Vì vậy, chúng ta có thể làm điều này:

    f2 = Partial(Add, 7);
    f2(5); // returns 12;
           // f2(7)(5) is just a syntactic shortcut
    
  • Curry chức năng Addsẽ cung cấp cho chúng ta một chức năng mới như đầu ra. Đó là chức năng riêng của mình mất 1 số như là đầu vào và đầu ra được nêu ra một chức năng mới. Hàm thứ ba đó sau đó lấy 1 số làm đầu vào và trả về một số làm đầu ra. Như vậy:

    Curry(Add); // returns a function f2 as output
    
                // f2 takes 1 number as input and returns a function f3 as output
                // i.e. f2(number) = f3
    
                // f3 takes 1 number as input and returns a number as output
                // i.e. f3(number) = number
    

    Vì vậy, chúng ta có thể làm điều này:

    f2 = Curry(Add);
    f3 = f2(7);
    f3(5); // returns 12
    

Nói cách khác, "currying" và "ứng dụng một phần" là hai chức năng hoàn toàn khác nhau. Currying mất chính xác 1 đầu vào, trong khi ứng dụng một phần mất 2 (hoặc nhiều hơn) đầu vào.

Mặc dù cả hai đều trả về một hàm là đầu ra, các hàm được trả về có các dạng hoàn toàn khác nhau như đã trình bày ở trên.


24
Ứng dụng một phần biến đổi một chức năng từ n-arysang (x - n)-ary, currying từ n-aryđến n * 1-ary. Một hàm được áp dụng một phần có phạm vi giảm (của ứng dụng), nghĩa Add7là ít biểu cảm hơn Add. Mặt khác, một hàm bị cong cũng biểu cảm như hàm ban đầu.
bob

4
Tôi tin rằng đặc điểm đặc biệt hơn là khi chúng ta cà ri f (x, y, z) => R, chúng ta nhận được f (x) trả về g (y) => h (z) => R, mỗi đối tượng tiêu thụ một đối số duy nhất; nhưng khi chúng ta áp dụng một phần f (x, y, z) là f (x), chúng ta sẽ có g (y, z) => R, nghĩa là có hai đối số. Nếu không phải vì đặc điểm đó, chúng ta có thể nói rằng currying giống như ứng dụng một phần cho 0 đối số, do đó không để lại tất cả các đối số; tuy nhiên trong thực tế f () được áp dụng một phần cho 0 đối số là một hàm tiêu thụ 3 đối số cùng một lúc, không giống như f ().
Maksim Gumerov

2
Một lần nữa, câu trả lời đúng không phải là câu đầu tiên hoặc được bình chọn nhiều nhất: Giải thích đơn giản về chữ ký của cà ri so với một phần ở cuối câu trả lời này thực sự là cách dễ nhất để giải quyết câu hỏi.
fnl

2
Ý kiến f2(7)(5) is just a syntactic shortcutcó nghĩa là gì? (Tôi biết rất ít.) Chưa f2có / "biết về" 7?
Zach Mierzejewski

@Pacerier, có curry triển khai ở đâu đó không (đừng nghĩ là ở đó functools)
alancalvitti

51

Lưu ý: điều này được lấy từ F # Basics một bài viết giới thiệu tuyệt vời cho các nhà phát triển .NET tham gia vào lập trình chức năng.

Currying có nghĩa là phá vỡ một hàm với nhiều đối số thành một chuỗi các hàm mà mỗi hàm lấy một đối số và cuối cùng tạo ra kết quả giống như hàm ban đầu. Currying có lẽ là chủ đề thách thức nhất đối với các nhà phát triển mới đối với lập trình chức năng, đặc biệt vì nó thường bị nhầm lẫn với ứng dụng một phần. Bạn có thể thấy cả hai tại nơi làm việc trong ví dụ này:

let multiply x y = x * y    
let double = multiply 2
let ten = double 5

Ngay lập tức, bạn sẽ thấy hành vi khác với hầu hết các ngôn ngữ bắt buộc. Câu lệnh thứ hai tạo ra một hàm mới gọi là double bằng cách truyền một đối số cho hàm có hai. Kết quả là một hàm chấp nhận một đối số int và mang lại cùng một đầu ra như thể bạn đã gọi phép nhân với x bằng 2 và y bằng với đối số đó. Về mặt hành vi, nó giống như mã này:

let double2 z = multiply 2 z

Thông thường, mọi người thường nhầm lẫn rằng nhân lên được uốn cong để tạo thành gấp đôi. Nhưng điều này chỉ đúng một chút. Hàm bội được xử lý, nhưng điều đó xảy ra khi nó được xác định bởi vì các hàm trong F # được mặc định theo chế độ. Khi chức năng kép được tạo, chính xác hơn để nói rằng chức năng nhân được áp dụng một phần.

Hàm bội thực sự là một chuỗi gồm hai hàm. Hàm đầu tiên nhận một đối số int và trả về một hàm khác, liên kết hiệu quả x với một giá trị cụ thể. Hàm này cũng chấp nhận một đối số int mà bạn có thể nghĩ là giá trị để liên kết với y. Sau khi gọi hàm thứ hai này, x và y đều bị ràng buộc, do đó, kết quả là tích của x và y như được định nghĩa trong thân kép.

Để tạo gấp đôi, hàm đầu tiên trong chuỗi các hàm nhân được đánh giá để áp dụng một phần nhân. Hàm kết quả được đặt tên kép. Khi đánh giá gấp đôi, nó sử dụng đối số của nó cùng với giá trị được áp dụng một phần để tạo kết quả.


33

Câu hỏi thú vị. Sau một chút tìm kiếm, "Ứng dụng chức năng một phần không phải là cà ri" đã đưa ra lời giải thích tốt nhất mà tôi tìm thấy. Tôi không thể nói rằng thực tế khác biệt đặc biệt rõ ràng đối với tôi, nhưng sau đó tôi không phải là chuyên gia về FP ...

Một trang tìm kiếm hữu ích khác (mà tôi thú nhận rằng tôi chưa đọc hết) là "Ứng dụng Currying và Partial with Java Closures" .

Có vẻ như đây là cặp thuật ngữ bị nhầm lẫn rộng rãi, làm phiền bạn.


5
Liên kết đầu tiên là tại chỗ về sự khác biệt. Đây là một cái khác tôi thấy hữu ích: bit.ly/CurryingVersusPartialApplication
Jason Bunting

5
Currying là để làm với các bộ dữ liệu (biến một hàm lấy một đối số tuple thành một đối số có n đối số riêng biệt và ngược lại). Ứng dụng một phần là khả năng áp dụng một hàm cho một số đối số, mang lại một hàm mới cho các đối số còn lại. Thật dễ nhớ nếu bạn chỉ nghĩ currying == để làm với tuples.
Don Stewart

9
@Jon liên kết bạn đã đăng là thông tin, nhưng sẽ tốt hơn để mở rộng câu trả lời của bạn và thêm một số thông tin ở đây.
Zaheer Ahmed

2
@Jon

11
Không thể tin rằng bạn có 20 lượt upvote cho một vài liên kết và một lần nhập học mà bạn không thực sự biết sự khác biệt giữa cà ri và ứng dụng một phần. Chơi tốt, thưa ngài.
AlienWebguy

16

Tôi đã trả lời điều này trong một chủ đề khác https://stackoverflow.com/a/12846865/1685865 . Nói tóm lại, ứng dụng hàm một phần là về việc sửa một số đối số của hàm đa biến nhất định để mang lại một hàm khác có ít đối số hơn, trong khi Currying là về việc biến một hàm của N đối số thành hàm unary trả về hàm unary ... [Một ví dụ về Currying được hiển thị ở cuối bài này.]

Currying chủ yếu là lợi ích lý thuyết: người ta có thể diễn tả các tính toán chỉ sử dụng các hàm unary (tức là mọi hàm đều là unary). Trong thực tế và như một sản phẩm phụ, nó là một kỹ thuật có thể làm cho nhiều ứng dụng chức năng một phần (nhưng không phải tất cả) trở nên tầm thường, nếu ngôn ngữ có chức năng bị quấy rối. Một lần nữa, nó không phải là phương tiện duy nhất để thực hiện các ứng dụng một phần. Vì vậy, bạn có thể gặp các tình huống trong đó ứng dụng một phần được thực hiện theo cách khác, nhưng mọi người đang nhầm nó là Currying.

(Ví dụ về Currying)

Trong thực tế người ta sẽ không chỉ viết

lambda x: lambda y: lambda z: x + y + z

hoặc javascript tương đương

function (x) { return function (y){ return function (z){ return x + y + z }}}

thay vì

lambda x, y, z: x + y + z

vì lợi ích của Currying.


1
Bạn có nói rằng cà ri là một trường hợp cụ thể của ứng dụng một phần không?
SpoonMeiser

1
@SpoonMeiser, Không, currying không phải là trường hợp cụ thể của ứng dụng một phần: Ứng dụng một phần của hàm 2 đầu vào không giống như currying hàm. Xem stackoverflow.com/a/23438430/632951 .
Pacerier

10

Currying là một hàm của một đối số có một hàm fvà trả về một hàm mới h. Lưu ý rằng hlấy một đối số từ Xvà trả về một hàm ánh xạ Ytới Z:

curry(f) = h 
f: (X x Y) -> Z 
h: X -> (Y -> Z)

Ứng dụng một phần là một hàm gồm hai (hoặc nhiều) đối số có chức năng fvà một hoặc nhiều đối số bổ sung fvà trả về một hàm mới g:

part(f, 2) = g
f: (X x Y) -> Z 
g: Y -> Z

Sự nhầm lẫn xuất hiện bởi vì với hàm hai đối số, đẳng thức sau giữ:

partial(f, a) = curry(f)(a)

Cả hai bên sẽ mang lại chức năng một đối số.

Bình đẳng là không đúng đối với các hàm arity cao hơn vì trong trường hợp này, currying sẽ trả về hàm một đối số, trong khi ứng dụng một phần sẽ trả về hàm nhiều đối số.

Sự khác biệt cũng nằm ở hành vi, trong khi việc curry biến đổi toàn bộ hàm ban đầu (một lần cho mỗi đối số), ứng dụng một phần chỉ là một bước thay thế.

Nguồn: Wikipedia Currying .


8

Sự khác biệt giữa cà ri và ứng dụng một phần có thể được minh họa rõ nhất qua ví dụ JavaScript sau đây:

function f(x, y, z) {
    return x + y + z;
}

var partial = f.bind(null, 1);

6 === partial(2, 3);

Ứng dụng một phần dẫn đến một chức năng của arity nhỏ hơn; trong ví dụ trên, fcó số điểm là 3 trong khi partialchỉ có mức độ là 2. Quan trọng hơn, một hàm được áp dụng một phần sẽ trả về kết quả ngay khi được gọi , không phải là một hàm khác trong chuỗi currying. Vì vậy, nếu bạn đang thấy một cái gì đó như partial(2)(3), nó không phải là một phần ứng dụng trong thực tế.

Đọc thêm:


"một hàm được áp dụng một phần sẽ trả về kết quả ngay khi được gọi" - điều đó không đúng, phải không? Khi tôi áp dụng một phần hàm, biểu thức đó trả về một hàm, không phải "kết quả". Ok, bạn có thể có nghĩa là hàm sau này, khi được gọi với các đối số còn lại, trả về kết quả, không giống như đào một bước xuống cà ri. Nhưng không ai thực sự nói rằng bạn phải chỉ định tất cả các đối số còn lại: bạn có thể áp dụng một phần kết quả của ứng dụng một phần và đó sẽ lại là một hàm, không phải là "kết quả"
Maksim Gumerov

6

Câu trả lời đơn giản

Curry: cho phép bạn gọi một chức năng, chia nhỏ nó trong nhiều cuộc gọi, cung cấp một đối số cho mỗi cuộc gọi.

Một phần: cho phép bạn gọi một hàm, tách nó trong nhiều cuộc gọi, cung cấp nhiều đối số cho mỗi cuộc gọi.


Gợi ý đơn giản

Cả hai đều cho phép bạn gọi một hàm cung cấp ít đối số hơn (hoặc, tốt hơn, cung cấp chúng một cách tích lũy). Trên thực tế cả hai liên kết (tại mỗi cuộc gọi) một giá trị cụ thể cho các đối số cụ thể của hàm.

Sự khác biệt thực sự có thể được nhìn thấy khi hàm có nhiều hơn 2 đối số.


Đơn giản e (c) (mẫu)

(trong Javascript)

function process(context, success_callback, error_callback, subject) {...}

Tại sao luôn luôn vượt qua các đối số, như bối cảnh và các cuộc gọi lại, nếu chúng sẽ luôn giống nhau? Chỉ cần liên kết một số giá trị cho hàm

processSubject = _.partial(process, my_context, my_success, my_error)

và gọi nó trên chủ đề1foobar với

processSubject('subject1');
processSubject('foobar');

Comfy, phải không? 😉

Với cà ri, bạn cần phải vượt qua một đối số mỗi lần

curriedProcess = _.curry(process);
processWithBoundedContext = curriedProcess(my_context);
processWithCallbacks = processWithBoundedContext(my_success)(my_error); // note: these are two sequential calls

result1 = processWithCallbacks('subject1');
// same as: process(my_context, my_success, my_error, 'subject1');
result2 = processWithCallbacks('foobar'); 
// same as: process(my_context, my_success, my_error, 'foobar');

Khước từ

Tôi bỏ qua tất cả các giải thích học thuật / toán học. Vì tôi không biết điều đó. Có lẽ nó đã giúp


4

Tôi đã có câu hỏi này rất nhiều trong khi học và đã được hỏi nó nhiều lần. Cách đơn giản nhất tôi có thể mô tả sự khác biệt là cả hai đều giống nhau :) Hãy để tôi giải thích ... rõ ràng có sự khác biệt.

Cả ứng dụng một phần và currying đều liên quan đến việc cung cấp các đối số cho một hàm, có lẽ không phải tất cả cùng một lúc. Một ví dụ khá kinh điển là thêm hai số. Trong mã giả (thực ra là JS không có từ khóa), hàm cơ sở có thể là như sau:

add = (x, y) => x + y

Nếu tôi muốn một chức năng "addOne", tôi có thể áp dụng một phần hoặc sử dụng nó:

addOneC = curry(add, 1)
addOneP = partial(add, 1)

Bây giờ sử dụng chúng là rõ ràng:

addOneC(2) #=> 3
addOneP(2) #=> 3

Vậy sự khác biệt là gì? Chà, nó tinh tế, nhưng ứng dụng một phần liên quan đến việc cung cấp một số đối số và hàm được trả về sau đó sẽ thực thi chức năng chính theo lệnh gọi tiếp theo trong khi curry sẽ tiếp tục chờ cho đến khi nó có tất cả các đối số cần thiết:

curriedAdd = curry(add) # notice, no args are provided
addOne = curriedAdd(1) # returns a function that can be used to provide the last argument
addOne(2) #=> returns 3, as we want

partialAdd = partial(add) # no args provided, but this still returns a function
addOne = partialAdd(1) # oops! can only use a partially applied function once, so now we're trying to add one to an undefined value (no second argument), and we get an error

Nói tóm lại, sử dụng ứng dụng một phần để điền trước một số giá trị, biết rằng lần sau khi bạn gọi phương thức, nó sẽ thực thi, để lại tất cả các đối số không được xác định; sử dụng currying khi bạn muốn liên tục trả về một hàm được áp dụng một phần nhiều lần nếu cần để hoàn thành chữ ký hàm. Một ví dụ cuối cùng:

curriedAdd = curry(add)
curriedAdd()()()()()(1)(2) # ugly and dumb, but it works

partialAdd = partial(add)
partialAdd()()()()()(1)(2) # second invocation of those 7 calls fires it off with undefined parameters

Hi vọng điêu nay co ich!

CẬP NHẬT: Một số ngôn ngữ hoặc triển khai lib sẽ cho phép bạn chuyển một arity (tổng số đối số trong đánh giá cuối cùng) sang triển khai ứng dụng một phần có thể kết hợp hai mô tả của tôi thành một mớ hỗn độn ... nhưng tại thời điểm đó, hai kỹ thuật là phần lớn có thể hoán đổi cho nhau.


3

Đối với tôi, ứng dụng một phần phải tạo một hàm mới trong đó các đối số được sử dụng được tích hợp hoàn toàn vào hàm kết quả.

Hầu hết các ngôn ngữ chức năng thực hiện currying bằng cách trả lại một bao đóng: không đánh giá theo lambda khi áp dụng một phần. Vì vậy, để ứng dụng một phần trở nên thú vị, chúng ta cần tạo sự khác biệt giữa ứng dụng cà ri và ứng dụng một phần và coi ứng dụng một phần là đánh giá cà ri cộng với đánh giá theo lambda.


3

Tôi có thể rất sai ở đây, vì tôi không có nền tảng vững chắc về toán học lý thuyết hoặc lập trình chức năng, nhưng từ bước đột phá ngắn ngủi của tôi vào FP, dường như việc currying có xu hướng biến một hàm N đối số thành N hàm của một đối số, trong khi ứng dụng một phần [trong thực tế] hoạt động tốt hơn với các hàm matrixdic với số lượng đối số không xác định. Tôi biết một số ví dụ trong các câu trả lời trước đã bất chấp lời giải thích này, nhưng nó đã giúp tôi nhiều nhất để tách các khái niệm. Hãy xem xét ví dụ này (được viết bằng CoffeeScript vì sự cô đọng, lời xin lỗi của tôi nếu nó gây nhầm lẫn thêm, nhưng vui lòng yêu cầu làm rõ, nếu cần):

# partial application
partial_apply = (func) ->
  args = [].slice.call arguments, 1
  -> func.apply null, args.concat [].slice.call arguments

sum_variadic = -> [].reduce.call arguments, (acc, num) -> acc + num

add_to_7_and_5 = partial_apply sum_variadic, 7, 5

add_to_7_and_5 10 # returns 22
add_to_7_and_5 10, 11, 12 # returns 45

# currying
curry = (func) ->
  num_args = func.length
  helper = (prev) ->
    ->
      args = prev.concat [].slice.call arguments
      return if args.length < num_args then helper args else func.apply null, args
  helper []

sum_of_three = (x, y, z) -> x + y + z
curried_sum_of_three = curry sum_of_three
curried_sum_of_three 4 # returns a function expecting more arguments
curried_sum_of_three(4)(5) # still returns a function expecting more arguments
curried_sum_of_three(4)(5)(6) # returns 15
curried_sum_of_three 4, 5, 6 # returns 15

Đây rõ ràng là một ví dụ giả định, nhưng lưu ý rằng việc áp dụng một phần hàm chấp nhận bất kỳ số lượng đối số nào cho phép chúng ta thực thi một hàm nhưng với một số dữ liệu sơ bộ. Curry một chức năng tương tự nhưng cho phép chúng ta thực hiện chức năng tham số N thành từng phần cho đến khi, nhưng chỉ cho đến khi, tất cả các tham số N được tính.

Một lần nữa, đây là nhận của tôi từ những thứ tôi đã đọc. Nếu bất cứ ai không đồng ý, tôi sẽ đánh giá cao một nhận xét về lý do tại sao chứ không phải là một downvote ngay lập tức. Ngoài ra, nếu CoffeeScript khó đọc, vui lòng truy cập coffeescript.org, nhấp vào "thử coffeescript" và dán mã của tôi để xem phiên bản được biên dịch, có thể (hy vọng) có ý nghĩa hơn. Cảm ơn!


2

Tôi sẽ cho rằng hầu hết những người hỏi câu hỏi này đều đã quen thuộc với các khái niệm cơ bản nên họ không cần phải nói về điều đó. Đó là sự chồng chéo đó là phần khó hiểu.

Bạn có thể có thể sử dụng đầy đủ các khái niệm, nhưng bạn hiểu chúng cùng nhau vì sự mờ ảo khái niệm vô định hình nguyên tử giả này. Điều còn thiếu là biết ranh giới giữa chúng ở đâu.

Thay vì xác định mỗi người là gì, sẽ dễ dàng hơn để làm nổi bật sự khác biệt của họ về ranh giới.

Currying là khi bạn xác định chức năng.

Ứng dụng một phần là khi bạn gọi hàm.

Ứng dụng là math-speak để gọi một hàm.

Ứng dụng một phần yêu cầu gọi một hàm curried và lấy một hàm làm kiểu trả về.


1

Có những câu trả lời tuyệt vời khác ở đây nhưng tôi tin rằng ví dụ này (theo sự hiểu biết của tôi) trong Java có thể có ích cho một số người:

public static <A,B,X> Function< B, X > partiallyApply( BiFunction< A, B, X > aBiFunction, A aValue ){
    return b -> aBiFunction.apply( aValue, b );
}

public static <A,X> Supplier< X > partiallyApply( Function< A, X > aFunction, A aValue ){
    return () -> aFunction.apply( aValue );
}

public static <A,B,X> Function<  A, Function< B, X >  > curry( BiFunction< A, B, X > bif ){
    return a -> partiallyApply( bif, a );
}

Vì vậy, currying cung cấp cho bạn một hàm một đối số để tạo các hàm, trong đó ứng dụng một phần tạo ra một hàm bao bọc mã hóa một hoặc nhiều đối số.

Nếu bạn muốn sao chép và dán, phần sau sẽ ồn ào hơn nhưng thân thiện hơn để làm việc vì các loại nhẹ nhàng hơn:

public static <A,B,X> Function< ? super B, ? extends X > partiallyApply( final BiFunction< ? super A, ? super B, X > aBiFunction, final A aValue ){
    return b -> aBiFunction.apply( aValue, b );
}

public static <A,X> Supplier< ? extends X > partiallyApply( final Function< ? super A, X > aFunction, final A aValue ){
    return () -> aFunction.apply( aValue );
}

public static <A,B,X> Function<  ? super A,  Function< ? super B, ? extends X >  > curry( final BiFunction< ? super A, ? super B, ? extends X > bif ){
    return a -> partiallyApply( bif, a );
}

Phần sau đây cung cấp cho tôi thông tin chi tiết chính: "Vì vậy, currying cung cấp cho bạn hàm một đối số để tạo các hàm, trong đó ứng dụng một phần tạo ra hàm bao bọc mã hóa một hoặc nhiều đối số."
Roland

0

Trong bài viết này, tôi nhầm lẫn cà ri và khó chịu. Chúng là các phép biến đổi nghịch đảo trên các hàm. Nó thực sự không quan trọng bạn gọi nó là gì, miễn là bạn có được những gì biến đổi và nghịch đảo của nó đại diện.

Uncurrying không được định nghĩa rất rõ ràng (hay đúng hơn, có những định nghĩa "xung đột" mà tất cả đều nắm bắt được tinh thần của ý tưởng). Về cơ bản, nó có nghĩa là biến một hàm lấy nhiều đối số thành một hàm lấy một đối số duy nhất. Ví dụ,

(+) :: Int -> Int -> Int

Bây giờ, làm thế nào để bạn biến điều này thành một hàm có một đối số duy nhất? Bạn gian lận, tất nhiên!

plus :: (Int, Int) -> Int

Lưu ý rằng cộng bây giờ có một đối số duy nhất (bao gồm hai điều). Siêu!

Điểm này là gì? Chà, nếu bạn có một hàm có hai đối số và bạn có một cặp đối số, thật tuyệt khi biết rằng bạn có thể áp dụng hàm cho các đối số và vẫn nhận được những gì bạn mong đợi. Và trên thực tế, hệ thống ống nước để thực hiện nó đã tồn tại, do đó bạn không phải làm những việc như khớp mẫu rõ ràng. Tât cả nhưng điêu bạn phải lam la:

(uncurry (+)) (1,2)

Vậy ứng dụng chức năng một phần là gì? Đó là một cách khác nhau để biến một hàm trong hai đối số thành một hàm với một đối số. Nó hoạt động khác nhau mặc dù. Một lần nữa, hãy lấy (+) làm ví dụ. Làm thế nào chúng ta có thể biến nó thành một hàm lấy một Int làm đối số? Chúng tôi gian lận!

((+) 0) :: Int -> Int

Đó là chức năng thêm số không vào bất kỳ Int.

((+) 1) :: Int -> Int

thêm 1 vào bất kỳ Int. V.v. Trong mỗi trường hợp này, (+) được "áp dụng một phần".

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.