NỘI DUNG BÀI VIẾT
Trong lập trình, thông thường để hoán đổi hai giá trị phải sử dụng 1 biến phụ và mất tới 3 dòng lệnh. Bạn có ngạc nhiên không nếu có một cách đơn giản hơn nhiều để hoán đổi hai giá trị mà không cần biến phụ mà chúng ta có thể sử dụng được trong JavaScript?
Sẽ có hai loại hoán đổi ở đây. Loại thứ nhất là hoán đổi biến kiểu nguyên thủy, ta sẽ hoán đổi hai số trước. Loại còn lại phức tạp hơn, ta sẽ hoán đổi hai biến kiểu bất kỳ: Object, String, Float,…
Phương pháp truyền thống
Mục tiêu ở đây ta sẽ hoán đổi hai biến a và b (kiểu bất kỳ)
let a = 1, b = 2, temp = 0; // swap temp = a; a = b; b = temp;
Như thường lệ, ta sử dụng một biến temp, tạm thời lưu trữ giá trị ban đầu của a rồi sau đó tiến hành hoán đổi. Nhưng liệu có cách nào mà không phải sử dụng biến tạm thời không? Có, có chứ!
Đối với số nguyên
Hãy bắt đầu với loại đầu tiên: hoán đổi hai số nguyên.
Sử dụng toán tử toán học
a = a + b; b = a - b; a = a - b;
Hoặc:
a += b; b = a - b; a -= b;
Sử dụng toán tử XOR
Với biến kiểu số nguyên, ta có thể khéo léo sử dụng toán tử đảo bit là XOR:
a = a ^ b; b = a ^ b; a = a ^ b;
Hoặc:
a ^= b; b ^= a; a ^= b;
Đây gọi là thuật toán hoán đổi sử dụng XOR. Nguyên lý của nó được miêu tả cụ thể ở đây (Wikipedia).
Một vài mẹo hay ho khác:
Hoán đổi trên một dòng
a = b + (b = a, 0);
Cách này ta sử dụng comma operator (toán tử dấu phẩy) không cần biến phụ, không cần mảng, có thêm thắt một chút, và nó chạy rất nhanh. Trong thực tế đôi khi nó còn nhanh hơn cách dùng biến phụ nếu chạy trên một vài nền tảng. Và nó đúng với mọi loại số, không bao giờ tràn bộ nhớ, và cân luôn mấy trường hợp đặc biệt chẳng hạn Infinity hay NaN.
Các bước chạy cụ thể như sau:
- ( b = a, 0): gán giá trị ban đầu của a cho b, trả về số 0.
- a = b + 0: gán giá trị b (ban đầu) + 0 cho a, tức gán a = b.
Cách hoán đổi khác, cũng một dòng:
b = a + (a = b) - b;
Vẫn là hoán đổi một dòng, nhưng dùng XOR:
a = a ^ b ^ (b ^= (a ^ b));
Cách hoán đổi cho mọi kiểu dữ liệu
Loại thứ hai khó hơn một chút, ta sẽ hoán đổi giá trị hai biến bất kỳ.
Cách truyền thống, sử dụng một dòng lệnh
Mẹo này sử dụng một mảng để thực hiện hoán đổi.
a = [b, b = a][0];
Có vài thứ xảy ra trên cùng một dòng lệnh:
- Đầu tiên, ta khởi tạo một mảng với phần tử đầu là b, phần tử thứ hai là b.
- Gán giá trị phần tử thứ hai, b = a ngay khi vừa khởi tạo mảng.
- Gán giá trị của phần tử đầu trong mảng, tức giá trị của b cho a.
Phương pháp hoán đổi khác
Ở ES6 ta sử dụng self executing arrow function:
b = (a => a)(a, a = b);
Nếu là ES5 câu lệnh trên được viết là:
b = (function(a) { return a } )(a, a = b);
Phương pháp sử dụng cho ES6 trở đi
Kể từ ES6, ta có thể hoán đổi hai giá trị một cách đơn giản hơn rất nhiều, gọi là destructuring assignment như sau:
let a = 1; let b = 3; [a, b] = [b, a]; console.log(a); // 3 console.log(b); // 1
Các bạn có thể tham khảo các bài viết hay về JavaScript tại đây.
Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.