Curry function là gì? Một món "cà ri" ngon và làm sao để thưởng thức nó?

Curry function là gì? Một món "cà ri" ngon và làm sao để thưởng thức nó?

Tin ngắn hàng ngày dành cho bạn
  • Hôm qua đến nay, lượt truy cập tới từ Facebook tăng đột biến. Thường như thế là do ai đó chia sẻ bài viết của blog vào một nhóm nào đó.

    Cơ mà lần này là liên kết trực tiếp đến trang chủ luôn. Tò mò ghê, không biết ai chia sẻ, chia sẻ ở đâu nữa. Muốn biết để tìm hiểu "insight" ghê 🥹

    » Xem thêm
  • Mình mới phát hiện ra thư viện idb-keyval giúp triển khai cơ sở dữ liệu dạng key-value một cách đơn giản. Như đã chia sẻ trong chuỗi bài viết về quá trình làm OpenNotas, mình loay hoay đi tìm một loại cơ sở dữ liệu để lưu trữ mà xem chừng vất vả quá, cuối cùng chốt localForage.

    idb-keyval cũng tương tự như localForage nhưng có vẻ như nó đang làm tốt hơn một chút. Đơn cử là có hàm update để cập nhật dữ liệu, hình dung đơn giản là:

    update('counter', (val) => (val || 0) + 1);

    Chứ không như hàm set là thay thế dữ liệu mất tiêu luôn.

    » Xem thêm
  • Đầu xuân năm mới, xin phép khoe số tiền kiếm được sau 1 tháng đặt quảng cáo tại indieboosting.com 🥳🥳🥳

    » Xem thêm

Vấn đề

Đối với mỗi lập trình viên, việc viết một đoạn mã có thể sử dụng lại nhiều lần là điều luôn cần thiết. Mỗi người có những cách để hiện thực hoá chúng, ví như việc đóng thư viện các đoạn mã hữu ích chẳng hạn. Nhưng trong bài viết này chúng ta hãy nói ở tầm vi mô hơn đó là ở mức độ hàm.

Curry function là gì?

Khái niệm về curry function khá là đơn giản, nó chính là một hàm javascript bình thường trong đó nhận nhiều đối số bằng một loạt các hàm một đối số. Ví dụ:

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

const add1 = sum(1);
const result = add1(2);
console.log(result); // 3;

// Hoặc ngắn gọn hơn chúng ta cũng có thể viết:  
const result =  sum(1)(2);
console.log(result); // 3

Oh, cú pháp có vẻ lơi lạ lẫm ha. Vậy thì điều này mang lại lợi ích gì?

Tại sao lại curry function?

Có 2 điều mà curry function có thể mang lại:

  • Các hàm nhỏ hơn có thể diễn dãi vấn đề một cách rõ ràng hơn và tái sử dụng lại một cách dễ dàng, không lộn xộn
  • Các hàm được sử dụng xuyên suốt

Hãy để tôi ví dụ cho các bạn làm rõ 2 điều trên.

Các hàm nhỏ hơn diễn đạt tốt hơn

Ví dụ đầu tiên tôi có một mảng các đối tượng gồm có id và name, hãy lấy ra chỉ mỗi id:

const objects = [{ id: 1, name: "A" }, { id: 2, name: "B" }, { id: 3, name: "C" }];
const ids = objects.map(function (o) { return o.id; });

Thật đơn giản phải không? Nhưng có một vấn để ở đây function (o) { return o.id; } là đủ nhưng chưa tốt. Hãy làm cho nó tốt hơn:

function get(attr) {
  return function getAttr(obj) {
    return obj[attr];
  }
}
const ids = objects.map(get("id"));

get là một hàm curry, nó nhận vào một attr để phục vụ việc việc lấy ra attribute nào và một obj là đối tượng cần được lấy ra.

Có thể bạn sẽ cảm thấy hơi khó hiểu với cách viết ở ids kia, hãy nhớ lại hàm map của javascript một chút:

Array.map(function (i) { return i; });

.map nhận vào tham số là một hàm, hàm này có quyền truy cập vào mỗi phần tử trong mảng để xử lý.

Hay nói cách khác đây thực sự là những gì mà ids được làm:

const ids = objects.map(function (o) { return get("id")(o); });

Đến đây thì các bạn đã hiểu rồi chứ? Vậy thì tôi có một câu hỏi dành cho bạn: nếu như tôi muốn dùng lại hàm "lấy id ra khỏi objects" kia nhiều lần thì sẽ làm như thế nào? Có phải là như thế này:

function getIds(objs) {
  return objects.map(get("id"));
}

getIds(objects);

Chà có vẻ mọi thứ hoạt động, tuy nhiên nó vẫn có thể tốt hơn. Tại sao chúng ta không áp dụng curry function để tạo ra một hàm được gọi là "map" có tác dụng lặp qua các phần tử trong mảng?

function map(func) {
  return function exec(value) {
    return value.map(func);
  }
}

function getIds = map(get("id"));
getIds(objects);

Đến đây thì tôi thấy rằng, nếu các hàm cơ bản là curry thì tôi có thể dễ dàng tạo ra một hàm thực hiện một chức năng mới từ chúng. Và một điều nữa là đoạn mã được viết rất dễ đọc, nhìn vào biết ngay là làm gì.

Chức năng xuyên suốt

Một ưu điểm của curry function là nó cho phép mã của bạn tập trung vào chức năng của nó hơn là một hàm thông thường. Như ở các ví dụ trên, việc tạo ra các hàm map, get, getIds giúp các đoạn mã dễ đọc hơn vì nó phản ánh đúng nhiệm vụ mà nó sẽ làm.

Hãy xem xét một ví dụ: Tôi có một hàm lấy dữ liệu JSON từ một máy chủ là fetchData, đây là một hàm bất đồng bộ và sau khi nhận được phản hồi, tôi sẽ lấy ra posts và danh sách id của các posts.

fetchData()
  .then(JSON.parse)
  .then(function(data){ return data.posts })
  .then(function(posts){
    return posts.map(function(post){ return post.title })
  });

Có thể bạn sẽ có cách viết khác tôi, nhưng trong trường hợp này tôi muốn nhấn mạnh việc sử dụng curry function sẽ làm đoạn mã kia trông dễ hiểu hơn:

fetchData()
  .then(JSON.parse)
  .then(get("posts")
  .then(map(get("id")));

Vì lợi ích mang lại của curry function, cũng có khá là nhiều những thư viện hỗ trợ như curry, hàm curry của Ramda là một thư viện nổi tiếng cho lập trình hàm.

Tổng kết

Curry function là một cách để "chơi" với hàm, diễn dãi hàm một cách dễ hiểu với những lợi ích mà nó mang lại. Ngay từ bây giờ nếu bạn cảm thấy hứng thú thì hãy thử áp dụng nó vào trong dự án tiếp theo của mình.

Cao cấp
Hello

5 bài học sâu sắc

Mỗi sản phẩm đi kèm với những câu chuyện. Thành công của người khác là nguồn cảm hứng cho nhiều người theo sau. 5 bài học rút ra được đã thay đổi con người tôi mãi mãi. Còn bạn? Hãy bấm vào ngay!

Mỗi sản phẩm đi kèm với những câu chuyện. Thành công của người khác là nguồn cảm hứng cho nhiều người theo sau. 5 bài học rút ra được đã thay đổi con người tôi mãi mãi. Còn bạn? Hãy bấm vào ngay!

Xem tất cả

Đăng ký nhận thông báo bài viết mới

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.

Bình luận (0)

Nội dung bình luận...
Bấm hoặc cuộn mạnh để sang bài mới