Toán tử ba ngôi có điều kiện
Toán tử ba ngôi có điều kiện cho phép mô tả trong một biểu thức duy nhất hai tùy chọn tính toán, dựa trên một điều kiện nhất định. Cú pháp toán tử như sau:
điều kiện ? biểu thức_đúng : biểu thức_sai
Điều kiện logic phải được chỉ định trong toán hạng đầu tiên 'điều kiện'. Đây có thể là sự kết hợp tùy ý của các phép toán so sánh và phép toán logic. Cả hai nhánh phải có mặt.
Nếu điều kiện là đúng, biểu thức expression_true
sẽ được tính toán, còn nếu sai, biểu thức expression_false
sẽ được tính toán.
Toán tử này đảm bảo rằng chỉ một trong hai biểu thức expression_true
và expression_false
sẽ được thực thi.
WARNING
Kiểu của hai biểu thức phải giống hệt nhau, nếu không, sẽ có khả năng ép kiểu ngầm chúng.
Xin lưu ý rằng kết quả của việc xử lý biểu thức trong MQL5 luôn biểu thị một RValue
(trong C++, nếu chỉ có LValues
trong biểu thức, thì kết quả của toán tử cũng sẽ là LValue
). Do đó, mã sau được biên dịch tốt trong C++, nhưng lại đưa ra lỗi trong MQL5:
int x1, y1; ++(x1 > y1 ? x1 : y1); // '++' - l-value là bắt buộc
Các toán tử điều kiện có thể được lồng nhau, nghĩa là, được phép sử dụng một toán tử điều kiện khác làm điều kiện hoặc nhánh (expression_true
hoặc expression_false
). Đồng thời, không phải lúc nào cũng rõ ràng các điều kiện liên quan đến điều gì (nếu dấu ngoặc đơn không được sử dụng để biểu thị rõ ràng việc nhóm). Hãy xem xét các ví dụ từ ExprConditional.mq5
.
int x = 1, y = 2, z = 3, p = 4, q = 5, f = 6, h = 7;
int r0 = x > y ? z : p != 0 && q != 0 ? f / (p + q) : h; // 0 = f / (p + q)
2
Trong trường hợp này, điều kiện logic đầu tiên biểu diễn phép so sánh x > y
. Nếu đúng, nhánh có biến z
được thực thi. Nếu sai, điều kiện logic bổ sung p != 0 && q != 0
được kiểm tra, với hai tùy chọn biểu thức.
Dưới đây là một số toán tử khác, trong đó các điều kiện logic được viết hoa, trong khi các tùy chọn tính toán được viết thường. Để đơn giản, tất cả chúng đều được tạo thành các biến (từ ví dụ trên). Trong thực tế, mỗi thành phần trong ba thành phần có thể là một biểu thức phong phú hơn.
Đối với mỗi chuỗi, bạn có thể theo dõi kết quả thu được như thế nào, điều này đã được hiển thị trong bình luận.
bool A = false, B = false, C = true;
int r1 = A ? x : C ? p : q; // 4
int r2 = A ? B ? x : y : z; // 3
int r3 = A ? B ? C ? p : q : y : z; // 3
int r4 = A ? B ? x : y : C ? p : q; // 4
int r5 = A ? f : h ? B ? x : y : C ? p : q; // 2
2
3
4
5
6
Vì toán tử là liên kết phải, biểu thức phức hợp được phân tích từ phải sang trái, tức là cấu trúc cực phải với ba toán hạng được kết hợp bởi ?
và :
trở thành toán hạng của điều kiện bên ngoài được viết ở bên trái. Sau đó, xét đến phép thay thế này, biểu thức được phân tích lại từ phải sang trái, và cứ như vậy, cho đến khi thu được cấu trúc cấp trên hoàn chỉnh cuối cùng ?:
.
Do đó, các biểu thức trên được nhóm lại như sau (dấu ngoặc đơn biểu thị cách diễn giải ngầm của trình biên dịch; nhưng có thể thêm dấu ngoặc đơn như vậy vào các biểu thức để trực quan hóa mã nguồn, đây thực sự là cách tiếp cận được khuyến nghị).
int r0 = x > y ? z : ((p != 0 && q != 0) ? f / (p + q) : h);
int r1 = A ? x : (C ? p : q);
int r2 = A ? (B ? x : y) : z;
int r3 = A ? (B ? (C ? p : q) : y) : z;
int r4 = A ? (B ? x : y) : (C ? p : q);
int r5 = (A ? f : h) ? (B ? x : y) : (C ? p : q);
2
3
4
5
6
Đối với biến r5
, điều kiện đầu tiên A ? f : h
tính toán điều kiện logic cho biểu thức tiếp theo và do đó, được chuyển đổi thành bool. Vì A bằng false
, nên giá trị được lấy từ biến h
. Nó không bằng 0; do đó, điều kiện đầu tiên được coi là đúng. Điều này dẫn đến nhánh kích hoạt (B ? x : y)
, từ đó giá trị của biến y
được trả về, vì B
bằng false
.
Phải có tất cả 3 thành phần (một điều kiện và 2 phương án thay thế) trong toán tử. Nếu không, trình biên dịch sẽ tạo ra lỗi "unexpected token":
// ';' - mã thông báo không mong muốn
// ';' - ':' dấu hai chấm mong đợi
int r6 = A ? B ? x : y ; // thiếu giải pháp thay thế
2
3
Trong ngôn ngữ biên dịch, một mã thông báo là một phần không thể chia cắt của mã nguồn, có ý nghĩa hoặc mục đích độc lập, chẳng hạn như kiểu, định danh, ký tự chấm câu, v.v. Toàn bộ mã nguồn được trình biên dịch chia thành một chuỗi các mã thông báo. Các dấu hiệu của các toán tử được xem xét cũng là các mã thông báo. Trong mã trên, có hai ký hiệu ?
và phải có hai ký hiệu :
khớp với chúng, nhưng đó là ký hiệu duy nhất. Do đó, trình biên dịch "nói" rằng ký hiệu kết thúc câu lệnh ;
là quá sớm và "hỏi" chính xác là thiếu sót gì: "dấu hai chấm được mong đợi".
Vì toán tử điều kiện có mức ưu tiên rất thấp (13 trong bảng đầy đủ, xem Priority of Operations ), nên được khuyến nghị đặt trong dấu ngoặc đơn. Điều này giúp tránh các tình huống mà các toán hạng của toán tử điều kiện có thể bị "bắt" bởi các toán tử lân cận có mức ưu tiên cao hơn. Ví dụ, nếu chúng ta cần tính giá trị của một biến w
nhất định thông qua tổng của hai toán tử ba ngôi, một cách tiếp cận đơn giản có thể xuất hiện như sau:
int w = A ? f : h + B ? x : y ; // 1
Điều này sẽ hoạt động khác với những gì chúng ta nghĩ. Do mức độ ưu tiên cao hơn, tổng h + B
được coi là một biểu thức duy nhất. Xem xét việc phân tích cú pháp từ phải sang trái, tổng này xuất hiện như một điều kiện và được chuyển thành kiểu bool, thậm chí trình biên dịch còn cảnh báo là "biểu thức không phải boolean". Biên dịch viên thậm chí có thể trực quan hóa diễn giải bằng dấu ngoặc đơn:
int w = A ? f : (( h + B ) ? x : y ); // 1
Để giải quyết vấn đề này, chúng ta nên đặt dấu ngoặc đơn theo cách riêng của mình.
int v = ( A ? f : h ) + ( B ? x : y ); // 9
Việc lồng nhau sâu các toán tử có điều kiện ảnh hưởng xấu đến khả năng hiểu mã. Nên tránh việc lồng nhau vượt quá hai hoặc ba cấp độ.