Reduce và reducer

Hôm nay mình sẽ nói về reduce và reducer, một hàm rất hay trong lập trình. Mình sẽ trình bày những ứng dụng của nó vào việc lập trình

Khái niệm

Reducers là những hàm nhận vào một giá trị, sau đó trả về giá trị khác dựa trên giá trị nhập vào

Đây là một khái niệm trong lập trình hàm, trong đó các hàm số là những pure function (không có side-effect). Reducer vì vậy ( khi áp dụng trong những ngôn ngữ tự do như javascript) cũng nên là pure function

Ví dụ :

1
const increase = val => val+1;

Khái niệm reducer rất đơn giản, nhưng khi ứng dụng nó với hàm reduce, sẽ mang lại một hiệu quả cao.

Tính tổng một mảng

Dưới đây mình sẽ dùng 2 cách

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Mang ban dau
const arr = [2,4,6,9,2];

// Sử dụng for loop
let sum = 0;
for (let index = 0; index < arr.length; index++)
{
sum+= arr[index];
}
console.log(sum);

// Su dung reduce
const initial = 0;
const reducer = (prev,current)=>pre+current;
const sum2 = arr.reduce(reducer,initial);
console.log(sum2);

Cụ thể hơn, nếu như sử dụng for loop, bạn sử dụng một biến để lưu tổng của mảng, và biến này sẽ thay đổi trong chương trình

1
2
3
4
5
6
0
2
6
12
21
23

Tuy nhiên với cách dùng reduce lại không thế, tổng tạm thời sau mỗi lần lặp đều được tạo mới và được truyền đến lần lặp sau (truyền giá trị ). Tổng cuối cùng chỉ được tạo ra sau lần lặp cuối cùng. Xem hình dưới đây.

Đệ quy

Hình dung dễ nhất về reduce, đó là sử dụng đệ quy. Bạn có thể thấy rõ hơn cách hoạt động của reduce.

1
2
3
4
5
6
7
8
9
const len = arr.length;
const recursi= (sum,val, index)=>{
if(index < len)
{
return recursi(sum + val,arr[index], index+1);
}
return sum;
}
console.log(recursi(0,arr[0],0))

Máy rút tiền

Vì hướng đối tượng của javascript chưa hỗ trợ biến private, nên mình sẽ viết ví dụ này trên C#

Lớp tài khoản. Ta có thể mở rộng bằng việc truyền những hàm khác nhau vào phương thức dispatch. Trong dispatch, ta có thể hoàn toàn kiểm soát việc thay đổi các trường ( giới hạn những trường được phép sửa). Dispatch giống như một setter, nhưng sẽ mạnh hơn.

1
2
3
4
5
6
7
8
9
10
11
12
13
class TaiKhoan
{
private int SoDu;
public TaiKhoan()
{
SoDu = 0;
}
public void dispatch(Func<int,int, int> reducer, int payLoad)
{
int soDuSau = reducer(SoDu,payLoad);
this.SoDu = soDuSau;
}
}

Chương trình chính

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Program
{
static void Main(string[] args)
{
var taiKhoan = new TaiKhoan();
//Rút
int Rut(int goc, int payload)
{
if (goc > payload)
{
return goc - payload;
}
Console.Write("Rút không thành công");
return goc;
};
//Nạp
int Nap(int goc, int payload) => goc + payload;
//Kiểm tra
int Check(int goc, int payload)
{
Console.Write("So tien :" + goc);
return goc;
}
taiKhoan.dispatch(Nap, 1000);
taiKhoan.dispatch(Check, 0);// 1000;
Console.WriteLine();
taiKhoan.dispatch(Rut, 100);
taiKhoan.dispatch(Check, 1000); // 900;
Console.Read();
}
}

Khi muốn sửa đổi một class, ta chỉ cần sửa đổi các reducer. Các reducer nên được viết càng dễ hiểu càng tốt, và nó thậm chí còn không biết được các bên trong class có gì.

Điểm yếu:

Kết

Với kiến thức hạn hẹp của mình, reduce, reducer là một cái gì đó rất cool và thú vị, nó mang lại những cách nghĩ mới, cách code mới. Hiện tại thì với reduce, nó chậm hơn đáng kể so với vòng lặp thông thường, nhưng nó khiễn code dễ đọc hơn rất nhiều và khiến cho việc kiểm soát lỗi tốt hơn.