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ó?

Những mẩu tin ngắn hàng ngày dành cho bạn
  • Hẳn là nhiều người ở đây đã nghe đến kiểu tấn công bảo mật Clickjacking rồi nhỉ. Kẻ tấn công thường nhúng một website (thường là mục tiêu) vào trong một iframe trên website của chúng, sau đó làm mờ hoặc ẩn nó đi rồi đặt vào vị trí các nút bấm trên web, ví dụ "Bấm vào để nhận quà". Đâu ai ngờ rằng phía trên nút bấm đó là một nút bấm khác trong iframe. Khá nguy hiểm!

    Nhưng trình duyệt đã có cách ngăn chặn kiểu tấn công này bằng các quy tắc như tiêu đề X-Frame-Options, frame-ancestors của CSP và SameSite: Lax/Strict của Cookies...

    Mới đây, đã xuất hiện thêm kiểu tấn công mới - "DoubleClickjacking" 😨. Đại ý là "hắn" lợi dụng hành động double click để lừa người dùng bấm vào một nút mà hắn muốn. Chi tiết hơn trong bài viết này: DoubleClickjacking: A New Era of UI Redressing.

    » Xem thêm
  • Mọi người đã nghe nói đến Jujutsu - jj - một dạng quản lý phiên bản cho mã nguồn (version control system) chưa? Có vẻ như nó đang nhận được nhiều sự quan tâm.

    Chờ xíu! Chẳng phải git đã quá tốt rồi sao? Thế thì chế ra thằng jj để làm gì nữa? Cũng hơi khó trả lời nhỉ? Mỗi công cụ sinh ra chắc chắn phải cải thiện hoặc khắc phục được nhược điểm của cái trước. Cho nên jj ắt hẳn phải làm được điều gì đó mà git chưa làm được nên mới nổi lên như vậy.

    Thật ra mình đã nghe nói đến jj từ vài tháng trước rồi, nhưng vào đọc thì toàn kiến thức cao siêu. Hoặc là đang mang nặng cái lối suy nghĩ của git vào trong đầu rồi nên chưa lĩnh hội ra được điều gì cả.

    Mình hay có kiểu cái gì đọc lần 1 mà không hiểu thì đọc tiếp lần 2, lần 2 không hiểu thì đọc tiếp lần 3... đến lần thứ n mà vẫn không hiểu thì bỏ. Cơ mà không phải là từ bỏ mà một thời gian sau đó quay lại đọc tiếp. Đến một lúc nào đó khả năng mình sẽ hiểu ra một ít vấn đề, thế mới tài 😆.

    Thì cái jj này có vẻ như nó đang mở ra được tính linh hoạt trong việc "cam kết" mã. Tưởng tượng bạn đang làm việc trên một dự án, đang ở nhánh này, muốn sang nhánh khác để sửa, nhưng mà lại đang viết dở ở nhánh này, thế là phải stash, rồi checkout, rồi commit, rồi merge hoặc rebase lại vào nhánh cũ... nhìn chung quá trình làm việc với git nghiêm ngặt đến mức cứng nhắc, cần nhiều thao tác để giải quyết một vấn đề, chưa kể cái cây commit (commit-tree) nữa thì ôi thôi, khỏi xem cho đỡ nhức mắt. Thế nên ông jj này đang làm cách nào đó để bạn khỏi cần phải quan tâm đến các nhánh luôn, sửa trực tiếp vào commit. Nghe ảo nhỉ 😂.

    Đấy mới lĩnh hội được đến đấy, hy vọng sau n lần đọc lại nữa mình sẽ viết được một bài chi tiết hơn về công cụ này.

    » Xem thêm
  • Gòi gòi tới công chiện gòi 🤤🤤🤤

    » 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