Short-circuit evaluation : Đoản mạch và chập điện

Bài viết hôm nay sẽ nhắc lại khái niệm “đoản mạch” của biểu thức logic trong lập trình. Mình dùng từ “nhắc lại” chứ không dùng từ “giới thiệu”, vì đây là điều ai cũng được học trong quá trình học, và 90% đều không áp dụng trong quá trình lập trình. (Số liệu thật đấy nhé, không chém đâu).

Trước tiên, chúng ta cùng ôn lại một chút về 2 biểu thức logic cơ bản là AND(&&) va OR(||). Chúng ta đều biết false && true = false, true || false = true. Điều ngược lại: true && false = false, false || true = true. Về logic, điều này đúng 100%, tuy nhiên bạn hãy thử điều này trong lập trình.

Giả sử ta có 2 hàm dưới đây, 1 hàm trả ra true, 1 hàm trả ra false

 static bool ReturnTrue()
 {
   Console.WriteLine("ReturnTrue");
   return true;
 }

 static bool ReturnFalse()
 {
   Console.WriteLine("ReturnFalse");
   return false;
 }

Đố bạn, đoạn code 1,2 và 3,4 sau đây cho kết quả giống hay khác nhau:

if (ReturnTrue() && ReturnFalse()) { }
if (ReturnFalse() && ReturnTrue()) { }

if (ReturnTrue() || ReturnFalse()) { }
if (ReturnFalse() || ReturnTrue()) { }

Đọc thử code, bạn sẽ nhủ thầm: Mẹ, dễ thế cũng đố, ai chẳng biết a && b = b && a, a || b = b || a. Đương nhiên là giống nhau rồi. Hãy chạy thử chương trình, bạn sẽ trợn mắt há mồm khi thấy kết quả:

if (ReturnTrue() && ReturnFalse()) { }  // Return True    Return False
if (ReturnFalse() && ReturnTrue()) { } // Return False

if (ReturnTrue() || ReturnFalse()) { } // Return True
if (ReturnFalse() || ReturnTrue()) { } // Return False Return True

Tới đây, nếu chưa biết về short-circuit, thể nào bạn cũng: clgt(Cần lời giải thích). Đây là lời giải thích của mình: Trong 1 số ngôn ngữ lập trình (C#, Java, Python), biểu thức logic thường có tính đoản mạch (Short-circuit).

Nói đơn giản là, với biểu thức AND, khi chương trình tìm thấy 1 điều kiện false, biểu thức AND sẽ trả kết quả false, những điều kiện sau điều kiện false đó sẽ không được thực thi. Ở dòng code thứ 2, ReturnTrue không chạy. Tương tự, ở dòng code thứ 3, khi nhận được kết quả True, biểu thức OR sẽ trả kết quả true, do đó ReturnFalse ko chạy.

Đọc tới đây, bạn sẽ gật gù như ngẫm ra điều gì đó, rồi tự nhủ: “Ờ, cái này hay ghê, giờ mới biết, nhưng mà cũng vô dụng thôi, có áp dụng vào code bao giờ đâu”. Trước tiên, xin mời bạn xem thử dòng code dưới đây. Ta nhận về một list, xử lý list đó:

List<string> list = GetList();
 if (list != null)
 {
   if (list.Count() != 0)
   {
     //Xử lý list
   }
 }

Ta có thể thấy người viết đã xử lý khá cẩn thận, nếu chỉ dùng hàm list.Count sẽ có khả năng gây ra NullPointerException, do đó ta phải check list != null trước. Tuy nhiên, khi đã biết về tính đoản mạch của biểu thức, ta hoàn toàn có thể viết như sau mà không sợ chương trình bị lỗi:

List<string> list = GetList();
//Nếu list null, chương trình sẽ không chạy
//câu lệnh list.Count, do đó không gây lỗi
 if (list != null && list.Count() != 0)
 {
     //Xử lý list
 }

Một ví dụ khác, code trước và sau khi áp dụng short-circuit, áp dụng cho việc so sánh chuỗi.

string name = "Hoang cute";
string search = "cute";

//Code cũ
 if (search != null)
 {
   if (name.ToLower().Contains(search.ToLower()))
   {
     Console.WriteLine("Found");
   }
 }

//Code mới
 if (search != null && name.ToLower().Contains(search.ToLower()))
 {
   Console.WriteLine("Found");
 }

Bài viết đến đây là kết thúc. Đây chỉ là một điều nhỏ nhặt, nhưng nó giúp code của bạn đẹp + dễ đọc hơn “một chút”. Một anh senior từng nói với mình rằng, giữa coder giỏi và dở, đôi khi chỉ là thằng code giỏi rành nhiều cái “một chút” hơn mà thôi, các bạn đừng nên xem thường nó nhé =))).

5 thoughts on “Short-circuit evaluation : Đoản mạch và chập điện”

  1. Cái ví dụ sau cùng thì nên dùng string.Equals(search, name, StringComparison.OrdinalIgnoreCase) nhé. Một là không cần check null, 2 là không cần chuyển sang LowerCase (Cái này ảnh hưởng performace kha khá nếu string dài)

    Like

  2. 🙂 cái này học được qua nhiều lần dính null pointer exception. Dùng nhiều qua kinh nghiệm mà cũng k biết nó tên là short-circuit 😀

    Like

Leave a comment