Hỏi về toán tử 3 ngôi - ternary operation

Chào mọi người :slight_smile:
Mình có 2 câu hỏi :

Thứ nhất :

  • Tại sao 2 biểu thức dưới đây lại tương đương nhau, vì theo mình biết, toán từ ? : có thứ tự kết hợp từ phải sang trái :

    • e = (a >= 40?'A':(a >= 30?'B':(a >= 20?'C':(a >= 10?'D':'F')))) (1)
    • e = (a >= 40?'A':a >= 30?'B':a >= 20?'C':a >= 10?'D':'F') (2)
  • Theo mình hiểu khi viết như (2) thì quá trình thực hiện sẽ thế này :
    e = a >= 40?'A':a >= 30?'B':a >= 20?'C':a >= 10?'D':'F';

    • Gia sử nhập a = 25, thì quá trình thực hiện như sau:
    • Đầu tiên nó kiếm tra a với 10 và rút gọn lại
    • a >= 40?'A':a >= 30?'B':a >= 20?'C':'D'
    • Tiếp theo kiếm tra a với 20
    • a >= 40?'A':a >= 30?'B': 'C'
    • Tiếp theo kiêm tra a voi 30
    • a >= 40?'A':'C'
    • và cuối cùng trả về C với giá trị nhập vào là 25.
  • Vậy có thể kết luận là, dù với giá trị nhập vào là bao nhiêu đi nữa thì biếu thức luôn kiêm tra bằng số điều kiện: (ở đây có 4 điều kiện ) – do vậy toán từ này sẽ chậm hơn if/else lồng nhau

  • Nhưng mình vẫn không hiểu sao thầy lại nói cách diễn giải trên của mình vẫn là từ trái sang phải

Thứ hai:

  • Nếu mình hiểu sai, thì biểu thức đúng để nó thực hiện từ phải sang trái mà vẫn giữ nguyên ý tưởng của bài là gì.

Cám ơn mọi người.

2 Likes

Rõ ràng là từ trái sang phải mà, đến điều kiện nào true là dừng thôi :kissing:

3 Likes

Ta có cú pháp ternary trong C/C++ (PHP rất điên) như sau:

<điều kiện>? <biểu thức 1> : <biểu thức 2>

với <biểu thức 1> ngầm hiểu là trong cặp ngoặc đơn.

Tiếp theo, nếu <điều kiện> không thỏa thì <biểu thức 2> mới được tính. Như vậy các <điều kiện> vẫn được tính từ trái sang phải thôi.


Ví dụ PHP :smiley:

echo (true ? 'true' : false ? 't' : 'f');

Vậy là có thể hiểu thêm khái niệm left-associative và right-associative rồi đó.

4 Likes

cám ơn bạn vì một ví dụ rất cụ thể và dễ hiểu, bây giờ mình đã biết biểu thức (1) và (2) như mình nói ở trên là từ trai sang phải rồi. Vậy nó có mẫu thuẫn gì với điều này:


ở đây nói rằng thứ tự kết hợp là từ phải sang trái.

Nếu cùng ví dụ trên mà mình muốn nó thực hiện theo thứ tự từ phải sang trái thì mình sẽ phải viết code thế nào, mong bạn giải đáp.

Cám ơn bạn nhiều nhiều.

xin lỗi vì mình chưa hiểu ý nghĩa của câu cuối

Mong được bạn giải đáp lại.
Mình cám ơn rất nhiều.

?: đánh giá từ trái sang phải vậy nhập 25 nó ktra với 40, sai nó ktra tiếp với 30, sai nó ktra tiếp với 20, đúng nó trả về ‘C’. Như đọc từ trái qua phải thôi chứ ở đâu ra mà ktra với 10 trước thế kia :V :V

đây em :V nó ktra theo thứ tự trái qua phải 40 30 20 10 đâu ra vừa vào nhảy vào ktra với 10 ~.~

3 Likes

vâng anh, em đang tìm trường hợp mà nó áp dụng thứ tự kết hợp từ phải sang trái ạ, còn đoạn anh nói thì em biết em sai rồi.

Thì trong PHP đó :smiley:

Left-associative:
a + b + c = (a + b) + c
a - b - c = (a - b) - c
a / b / c = (a / b) / c
Right-associative:
a = b = c: a = (b = c)

4 Likes

ok căm ơn bạn, đoạn này thì mình hiểu rồi. còn với thứ tự kết hợp phải sang trái của ?: thì mình chưa hình dung ra

Hình như ở đây bạn bị nhầm lần giữa thứ tự thực hiện và thứ tự kết hợp.

Với toán tử ?: thì kết hợp từ phải sang trái là đúng rồi:
C0?a:C1?b:c tương đương với C0?a:(C1?b:c)

Còn thứ tự thực hiện thì do ?: nó chỉ tính một bên của dấu : nên luôn phải tính điều kiện trước rồi mới xét tiếp được, nên nó vẫn xét C0 rồi mới quyết định tiếp là trả về a hay xét tiếp C1

3 Likes

Hình như là mình đang bị nhầm lẫn nếu 2 khái niệm đó là khác nhau, còn đoạn dưới đây bạn có thể giải thích rõ hơn giúp mình là nó kết hợp như thế nào.

cám ơn bạn.

Thì 2 cái đó khác nhau mà: kết hợp chính là thêm dấu ngoặc vào để “nhóm” các cụm vào với nhau:

Với C0?a:C1?b:c:

  • Nếu kết hợp từ trái sang phải sẽ ra: (C0?a:C1)?b:c
  • Nếu là kết hợp từ phải sang trái sẽ là: C0?a:(C1?b:c)

Mà theo “quy định” của chuẩn C++ thì nó là kết hợp từ phải sang trái, nên nó sẽ ra trường hợp sau thôi.

4 Likes

để lý giải cho suy luận trên, là mình áp dụng cách tính của hàm mũ:
hàm mũ cũng có thứ tự kết hợp từ phải sang trái, chẳng hạn có ví dụ:

4^5^6

thì đầu tiền là mình thực hiện 5^6 trước, xong lấy 4 mũ với kết quả nhận được ở trên. nó giống với thứ tự thực hiện.

Cái này làm sao bạn lại biết là nó tính “5^6” trước mà không phải “tính” 4 để ra 4 trước?
Lấy ví dụ: (1+2)^5^6 với ^ là hàm mũ thì chúng ta không thể xác định được là compiler sẽ tính 1+2 trước hay là 5^6 trước đâu bạn à. Chúng ta chỉ có thể khẳng định là nó sẽ tính 5^6 “kết hợp” thành 1 nhóm, chứ nó không “kết hợp” (1+2)^5 thành 1 nhóm. Đó mới là ý nghĩa của kết hợp.

Cái vụ thứ tự thực hiện, thì bên C++ có 1 keyword advance hơn nhiều là sequence point còn C thì có rule như này

6 Likes

cám ơn bạn, mĩnh đã hiểu rồi.

đoạn này bạn bị sai nhé, nếu nó tính xong mới xét tiếp đoạn sau thì sai hoàn toàn vì nếu như thế 2 đoạn code sau tương đương int a; 1?printf(“1”):a = 2; ( code bị lỗi) , int a; 1?printf(“1”):(a = 2)(code này chạy); vậy rõ ràng bạn bảo C0?a:C1?b:c tương đương với C0?a:(C1?b:c) là sai.

thực ra mình nghĩ đoạn này bạn nói tính 4 là bị sai. Trong môn cấu trúc dữ liệu và thuật toán hẳn bạn đã được học về stack, chắc chắn nếu bn học bạn sẽ học được bài là cây nhị phân biểu thức, có đề cập tới biểu thức prefix, infix và postfix. Thứ tự thực hiện các phép toán trong chương trình cũng như vậy. 4 không được “tính” mà sẽ được cho vào trong ngăn nhớ stack, và vì 1+2 ở trong ngoặc nên nó được thực hiện trước chứ không phải là " chúng ta không thể xác định được là compiler sẽ tính 1+2 trước hay là 5^6" như bạn nói. Mọi việc sẽ rất là dễ hiểu nếu bạn “có học” cấu trúc dữ liệu và thuật toán. Hi vọng bạn có thể tìm đọc lại để có câu trả lời xem là à, nó thực sự thực hiện từ phải qua trái “như thế nào”

Khi có các toán tử khác nhau thì căn cứ vào độ ưu tiên để thêm ngoặc chứ. Do = ưu tiên thấp hơn ?: nên sẽ trở thành (1?printf(“1”):a) = 2; // lỗi gán cho rvalue

2 Likes

Cái bạn nói nghe thì có vẻ rất đúng theo lý thuyết (và có thể theo thực tế), nhưng nếu bạn có đọc link về sequence point bên trên, bạn sẽ thấy ngay đoạn đầu tiên như này:

Order of evaluation of any part of any expression, including order of evaluation of function arguments is unspecified (with some exceptions listed below). The compiler can evaluate operands and other subexpressions in any order, and may choose another order when the same expression is evaluated again.

Dịch nôm na, có nghĩa là: ví dụ với biểu thức (1+2)+(3+4), C++ compiler hoàn toàn có quyền tính 3+4 trước, rồi mới tính 1+2, rồi cộng 2 kết quả kia lại với nhau, chứ không bắt buộc phải tính từ trái qua phải. Điều này quy định rõ trong C++ standard, nên là hy vọng bạn đọc và tìm hiểu kỹ trước để có mặt bằng chung thảo luận tiếp nhé.

Bên trên, mình dùng chữ "tính" 4, với tính trong ngoặc kép, kèm ví dụ (1+2)^5^6 là để thể hiện rõ: nếu 4 là một biểu thức khác, thì compiler có quyền chọn tính cái nào trước cũng được

1 Like

Ok thực ra mình đã xem trước khi bình luận, tuy nhiên trang này là một nguồn tài liệu tham khảo chứ không hoàn toàn chính xác hết được giả sử bạn code cho mình đoạn b = ++a + ++a + ++a với a=10 thì bạn nghĩ compiler sẽ trả kết quả bnh ? Và tại sao ko phải là 39 , hoàn toàn là do cài đặt ctdl gây ra side effects. ngoài ra việc compiler thực hiện phép tính nào trước thì bạn có vẻ đang hiểu sai ý mình, trong phép toán có sự kết hợp của các toán tử khác nhau thì chúng ta mới đang nói đến thứ tự thực hiện thì hiển nhiên 1+3*5 sẽ nhân 3 với 5 trước chứ làm gì phải là tùy ý như bạn nói? Còn ví dụ về việc tính của bạn thì có lẽ mình hiểu sai ý bạn nhưng mà trong ngoặc trước ngoài ngoặc sau còn về vde của bạn thì phép mũ thực hiện từ phải qua trái là rõ ràng còn việc 4^5 trước hay 5^6 trước thì hiển nhiên 5^6 trước chứ sao mà 4^5 trước được? Vde toán cơ bản mà vậy còn ví dụ bạn đưa ra ở bình luận mới, thì cứ cho là nó sẽ thực hiện tùy ý đi, nó cũng ko liên quan đến bình luận của bạn bên trên

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?