Tại sao hầu hết các ngôn ngữ chính không hỗ trợ cú pháp của xx y <z </> đối với các phép so sánh Boolean 3 chiều?


34

Nếu tôi muốn so sánh hai số (hoặc các thực thể được sắp xếp tốt khác), tôi sẽ làm như vậy với x < y. Nếu tôi muốn so sánh ba trong số họ, học sinh đại số trung học sẽ đề nghị thử x < y < z. Lập trình viên trong tôi sau đó sẽ trả lời "không, điều đó không hợp lệ, bạn phải làm x < y && y < z".

Hầu hết các ngôn ngữ tôi đi qua dường như không hỗ trợ cú pháp này, điều này thật kỳ lạ khi nó phổ biến trong toán học. Python là một ngoại lệ đáng chú ý. JavaScript trông giống như một ngoại lệ, nhưng nó thực sự chỉ là một sản phẩm phụ đáng tiếc của ưu tiên nhà điều hành và chuyển đổi ngầm định; trong node.js, 1 < 3 < 2đánh giá true, bởi vì nó thực sự (1 < 3) < 2 === true < 2 === 1 < 2.

Vì vậy, câu hỏi của tôi là: Tại sao x < y < zkhông phổ biến trong các ngôn ngữ lập trình, với ngữ nghĩa dự kiến?


1
Đây là tệp ngữ pháp mà họ sử dụng một cách khéo léo ngay trong tài liệu Python - Tôi không nghĩ nó khó đến thế: docs.python.org/reference/grammar.html
Aaron Hall

Tôi không biết các ngôn ngữ khác cũng như tôi biết Python, nhưng tôi có thể nói về sự đơn giản trong cách giải thích của Python về nó. Có lẽ tôi nên trả lời. Nhưng tôi không đồng ý với kết luận của gnasher729 về việc nó gây sát thương.
Aaron Hall

@ErikEidt - Nhu cầu có thể viết các biểu thức toán học theo cách chúng ta được dạy ở trường trung học (hoặc trước đó). Mọi người có khuynh hướng toán học đều biết $ a <b <c <d $ nghĩa là gì. Chỉ vì một tính năng tồn tại không có nghĩa là bạn phải sử dụng nó. Những người không thích nó luôn có thể đưa ra quy tắc cá nhân hoặc dự án cấm sử dụng.
David Hammen

2
Tôi nghĩ điều cần làm là nhóm C # (ví dụ) tốt hơn để khám phá LINQ và trong tương lai có thể ghi lại các kiểu và mẫu phù hợp hơn là thêm một số cú pháp giúp tiết kiệm cho mọi người 4 lần nhấn phím và không thực sự thêm bất kỳ biểu cảm nào (bạn cũng có thể viết helpermethod như static bool IsInRange<T>(this T candidate, T lower, T upper) where T : IComparable<T>nếu nó thực sự làm phiền bạn để xem &&s)
sara

1
SQL khá "chính thống" và bạn có thể viết "x trong khoảng từ 1 đến 10"
JoelFan

Câu trả lời:


30

Đây là các toán tử nhị phân, khi được xâu chuỗi, thông thường và tự nhiên tạo ra một cây cú pháp trừu tượng như:

cây cú pháp trừu tượng bình thường cho các toán tử nhị phân

Khi được đánh giá (mà bạn làm từ lá lên), điều này tạo ra kết quả boolean từ x < yđó, sau đó bạn gặp lỗi loại cố gắng thực hiện boolean < z. Để x < y < zlàm việc như bạn đã thảo luận, bạn phải tạo một trường hợp đặc biệt trong trình biên dịch để tạo ra một cây cú pháp như:

cây cú pháp trường hợp đặc biệt

Không phải là không thể làm điều này. Rõ ràng là như vậy, nhưng nó thêm một số phức tạp cho trình phân tích cú pháp cho một trường hợp không thực sự xuất hiện thường xuyên. Về cơ bản, bạn đang tạo một biểu tượng đôi khi hoạt động như một toán tử nhị phân và đôi khi hoạt động hiệu quả như một toán tử ternary, với tất cả các hàm ý của việc xử lý lỗi và do đó đòi hỏi. Điều đó thêm rất nhiều không gian cho những điều sai lầm mà các nhà thiết kế ngôn ngữ sẽ tránh nếu có thể.


1
"Sau đó, bạn gặp lỗi loại cố gắng thực hiện boolean <z" - không phải nếu trình biên dịch cho phép xâu chuỗi bằng cách đánh giá y tại chỗ để so sánh z. "Điều đó thêm rất nhiều không gian cho những điều sai lầm mà các nhà thiết kế ngôn ngữ sẽ tránh nếu có thể." Trên thực tế, Python không có vấn đề gì khi thực hiện điều này và logic để phân tích cú pháp được giới hạn trong một chức năng duy nhất: hg.python.org/cpython/file/tip/Python/ast.c#l1122 - không có nhiều không gian cho mọi thứ diễn ra sai rồi. "Đôi khi hoạt động như một toán tử nhị phân và đôi khi hoạt động hiệu quả như một toán tử nhị phân", trong Python, toàn bộ chuỗi so sánh là tạm thời.
Aaron Hall

2
Tôi chưa bao giờ nói điều đó là không thể, chỉ là làm thêm với sự phức tạp thêm. Các ngôn ngữ khác không phải viết bất kỳ mã riêng biệt nào chỉ để xử lý các toán tử so sánh của chúng. Bạn nhận được nó miễn phí với các nhà khai thác nhị phân khác. Bạn chỉ cần xác định quyền ưu tiên của họ.
Karl Bielefeldt

Có, nhưng ... có đã là một nhà điều hành ternary sẵn trong rất nhiều ngôn ngữ?
JensG

1
@JensG Việc biểu thị của ternary có nghĩa là phải mất 3 đối số. Trong ngữ cảnh liên kết của bạn, đó là một toán tử điều kiện thứ ba. Rõ ràng là "trinary" trong một thuật ngữ được đặt ra cho một toán tử dường như mất 2 nhưng thực sự mất 3. Vấn đề chính của tôi với câu trả lời này là chủ yếu là FUD.
Aaron Hall

2
Tôi là một trong những người phản đối về câu trả lời được chấp nhận này. (@JesseTG: Vui lòng không chấp nhận câu trả lời này.) Câu hỏi này gây nhầm lẫn về x<y<zý nghĩa, hoặc quan trọng hơn , x<y<=z. Câu trả lời này diễn giải x<y<znhư một toán tử ba. Đó chính xác là cách biểu thức toán học được xác định rõ này không nên được diễn giải. x<y<zthay vì tốc ký cho (x<y)&&(y<z). Các so sánh cá nhân vẫn là nhị phân.
David Hammen

37

Tại sao x < y < zkhông phổ biến trong các ngôn ngữ lập trình?

Trong câu trả lời này, tôi kết luận rằng

  • mặc dù cấu trúc này không quan trọng để thực hiện theo ngữ pháp của ngôn ngữ và tạo ra giá trị cho người dùng ngôn ngữ,
  • những lý do chính khiến điều này không tồn tại trong hầu hết các ngôn ngữ là do tầm quan trọng của nó so với các tính năng khác và sự không sẵn lòng của các cơ quan quản lý ngôn ngữ đối với một trong hai ngôn ngữ
    • làm người dùng khó chịu với những thay đổi có khả năng phá vỡ
    • để di chuyển để thực hiện tính năng (tức là: sự lười biếng).

Giới thiệu

Tôi có thể nói từ quan điểm của Pythonist về câu hỏi này. Tôi là người sử dụng ngôn ngữ có tính năng này và tôi thích nghiên cứu chi tiết triển khai ngôn ngữ. Ngoài ra, tôi có phần quen thuộc với quá trình thay đổi ngôn ngữ như C và C ++ (tiêu chuẩn ISO được điều chỉnh bởi ủy ban và được phiên bản theo năm.) Và tôi đã xem cả Ruby và Python thực hiện các thay đổi vi phạm.

Tài liệu và cách thực hiện của Python

Từ các tài liệu / ngữ pháp, chúng ta thấy rằng chúng ta có thể xâu chuỗi bất kỳ số lượng biểu thức nào với các toán tử so sánh:

comparison    ::=  or_expr ( comp_operator or_expr )*
comp_operator ::=  "<" | ">" | "==" | ">=" | "<=" | "!="
                   | "is" ["not"] | ["not"] "in"

và các tài liệu nêu thêm:

Việc so sánh có thể được xâu chuỗi tùy ý, ví dụ: x <y <= z tương đương với x <y và y <= z, ngoại trừ việc y chỉ được đánh giá một lần (nhưng trong cả hai trường hợp z đều không được đánh giá khi tìm thấy x <y là sai).

Tương đương logic

Vì thế

result = (x < y <= z)

là một cách logic tương đương về mặt đánh giá x, yz, với ngoại lệ mà yđược đánh giá hai lần:

x_lessthan_y = (x < y)
if x_lessthan_y:       # z is evaluated contingent on x < y being True
    y_lessthan_z = (y <= z)
    result = y_lessthan_z
else:
    result = x_lessthan_y

Một lần nữa, sự khác biệt là y chỉ được đánh giá một lần với (x < y <= z) .

(Lưu ý, các dấu ngoặc đơn là hoàn toàn không cần thiết và dư thừa, nhưng tôi đã sử dụng chúng vì lợi ích của những từ đến từ các ngôn ngữ khác và mã trên là Python khá hợp pháp.)

Kiểm tra cây cú pháp trừu tượng

Chúng ta có thể kiểm tra cách Python phân tích các toán tử so sánh chuỗi:

>>> import ast
>>> node_obj = ast.parse('"foo" < "bar" <= "baz"')
>>> ast.dump(node_obj)
"Module(body=[Expr(value=Compare(left=Str(s='foo'), ops=[Lt(), LtE()],
 comparators=[Str(s='bar'), Str(s='baz')]))])"

Vì vậy, chúng ta có thể thấy rằng điều này thực sự không khó đối với Python hoặc bất kỳ ngôn ngữ nào khác để phân tích.

>>> ast.dump(node_obj, annotate_fields=False)
"Module([Expr(Compare(Str('foo'), [Lt(), LtE()], [Str('bar'), Str('baz')]))])"
>>> ast.dump(ast.parse("'foo' < 'bar' <= 'baz' >= 'quux'"), annotate_fields=False)
"Module([Expr(Compare(Str('foo'), [Lt(), LtE(), GtE()], [Str('bar'), Str('baz'), Str('quux')]))])"

Và trái với câu trả lời hiện đang được chấp nhận, hoạt động ternary là một hoạt động so sánh chung, lấy biểu thức đầu tiên, một phép so sánh cụ thể và một nút lặp của các nút biểu thức để đánh giá khi cần thiết. Đơn giản.

Kết luận về Python

Cá nhân tôi thấy ngữ nghĩa phạm vi khá thanh lịch và hầu hết các chuyên gia Python mà tôi biết sẽ khuyến khích việc sử dụng tính năng này, thay vì xem xét nó có hại - ngữ nghĩa được nêu khá rõ trong tài liệu có uy tín (như đã lưu ý ở trên).

Lưu ý rằng mã được đọc nhiều hơn nó được viết. Những thay đổi giúp cải thiện khả năng đọc mã nên được chấp nhận, không được giảm giá bằng cách tăng các đối tượng chung về Sợ hãi, Không chắc chắn và Nghi ngờ .

Vậy tại sao x <y <z không phổ biến trong các ngôn ngữ lập trình?

Tôi nghĩ rằng có một sự hợp lưu của các lý do xoay quanh tầm quan trọng tương đối của tính năng và động lượng / quán tính tương đối của sự thay đổi được cho phép bởi các thống đốc của các ngôn ngữ.

Các câu hỏi tương tự có thể được hỏi về các tính năng ngôn ngữ quan trọng khác

Tại sao không có nhiều kế thừa có sẵn trong Java hoặc C #? Không có câu trả lời tốt ở đây cho một trong hai câu hỏi . Có lẽ các nhà phát triển đã quá lười biếng, như Bob Martin cáo buộc, và những lý do được đưa ra chỉ là lý do. Và thừa kế nhiều là một chủ đề khá lớn trong khoa học máy tính. Nó chắc chắn quan trọng hơn chuỗi vận hành.

Giải pháp đơn giản tồn tại

Chuỗi toán tử so sánh là thanh lịch, nhưng không có nghĩa là quan trọng như nhiều kế thừa. Và cũng giống như Java và C # có giao diện như một cách giải quyết, mọi ngôn ngữ cho nhiều so sánh - bạn chỉ cần xâu chuỗi các phép so sánh với boolean "và", hoạt động đủ dễ dàng.

Hầu hết các ngôn ngữ được điều chỉnh bởi ủy ban

Hầu hết các ngôn ngữ đang phát triển bởi ủy ban (thay vì có một Nhà độc tài nhân từ hợp lý cho cuộc sống như Python có). Và tôi suy đoán rằng vấn đề này chưa thấy đủ sự hỗ trợ để đưa nó ra khỏi các ủy ban tương ứng.

Các ngôn ngữ không cung cấp tính năng này có thể thay đổi không?

Nếu một ngôn ngữ cho phép x < y < zmà không có ngữ nghĩa toán học dự kiến, đây sẽ là một thay đổi đột phá. Nếu nó không cho phép nó ở nơi đầu tiên, nó sẽ gần như không đáng kể để thêm vào.

Thay đổi đột phá

Về các ngôn ngữ có các thay đổi vi phạm: chúng tôi cập nhật các ngôn ngữ có các thay đổi về hành vi - nhưng người dùng có xu hướng không thích điều này, đặc biệt là người dùng các tính năng có thể bị hỏng. Nếu người dùng đang dựa vào hành vi trước đây x < y < z, họ có thể sẽ phản đối mạnh mẽ. Và vì hầu hết các ngôn ngữ được điều hành bởi ủy ban, tôi nghi ngờ chúng ta sẽ có nhiều ý chí chính trị để hỗ trợ một sự thay đổi như vậy.


Thành thực mà nói, tôi mất không có vấn đề với ngữ nghĩa được cung cấp bởi các ngôn ngữ mà các hoạt động so sánh chuỗi như `x <y <z 'nhưng nó là tầm thường cho một nhà phát triển để tinh thần bản đồ x < y < zđể (x < y) && (y < z). Chọn nits, mô hình tinh thần cho việc so sánh chuỗi là toán học nói chung. So sánh cổ điển không phải là toán học nói chung, mà là logic Boolean. x < ytạo ra một câu trả lời nhị phân {0}. y < ztạo ra một câu trả lời nhị phân {1}. {0} && {1}tạo ra câu trả lời mô tả. Logic được sáng tác, không bị xiềng xích ngây thơ.
K. Alan Bates

Để giao tiếp tốt hơn, tôi đã mở đầu câu trả lời bằng một câu duy nhất tóm tắt trực tiếp toàn bộ nội dung. Đó là một câu dài, vì vậy tôi đã phá vỡ nó trong những viên đạn.
Aaron Hall

2
Lý do chính khiến ít ngôn ngữ thực hiện tính năng này là trước Guido, thậm chí không ai nghĩ về nó. Các ngôn ngữ kế thừa từ C không thể có được "đúng" (đúng về mặt toán học) bây giờ chủ yếu là do các nhà phát triển của C đã hiểu "sai" (sai về mặt toán học) hơn 40 năm trước. Có rất nhiều mã ngoài đó phụ thuộc vào bản chất trái ngược với cách các ngôn ngữ đó diễn giải x<y<z. Một ngôn ngữ đã từng có cơ hội để có được một cái gì đó như thế này đúng, và một cơ hội là ở sự khởi đầu của ngôn ngữ.
David Hammen

1
@ K.AlanBates Bạn thực hiện 2 điểm: 1) chuỗi vận hành là cẩu thả và 2) đường cú pháp không có giá trị. Đầu tiên: Tôi đã chứng minh rằng chuỗi vận hành là 100% xác định, phải không? Có lẽ một số lập trình viên quá lười biếng để mở rộng khả năng hiểu được cấu trúc? Đến điểm thứ hai: Nghe có vẻ như bạn đang tranh cãi trực tiếp với khả năng đọc? Không phải đường cú pháp thường được coi là một điều tốt khi nó cải thiện khả năng đọc? Nếu bình thường nghĩ theo cách này là bình thường, tại sao một lập trình viên lại không muốn giao tiếp như vậy? Mã nên được viết để được đọc, không?
Aaron Hall

2
I have watched both Ruby and Python implement breaking changes.Đối với những người tò mò, đây là một sự thay đổi phá vỡ trong C # 5.0 liên quan đến biến vòng lặp và đóng cửa: blogs.msdn.microsoft.com/ericlippert/2009/11/12/...
user2023861

13

Ngôn ngữ máy tính cố gắng xác định các đơn vị nhỏ nhất có thể và cho phép bạn kết hợp chúng. Đơn vị nhỏ nhất có thể sẽ là một cái gì đó như "x <y" mang lại kết quả boolean.

Bạn có thể yêu cầu một nhà điều hành ternary. Một ví dụ sẽ là x <y <z. Bây giờ những sự kết hợp của các nhà khai thác chúng ta cho phép? Rõ ràng x> y> z hoặc x> = y> = z hoặc x> y> = z hoặc có thể x == y == z nên được cho phép. Còn x <y> z thì sao? x! = y! = z? Cái cuối cùng có nghĩa là gì, x! = Y và y! = Z hoặc cả ba đều khác nhau?

Bây giờ khuyến mãi đối số: Trong C hoặc C ++, các đối số sẽ được thăng cấp thành một loại phổ biến. Vậy x <y <z có nghĩa là gì của x là gấp đôi nhưng y và z dài int? Cả ba thăng lên gấp đôi? Hoặc y được lấy gấp đôi một lần và dài như int lần khác? Điều gì xảy ra nếu trong C ++ một hoặc cả hai toán tử bị quá tải?

Và cuối cùng, bạn có cho phép bất kỳ số toán hạng nào không? Giống như một <b> c <d> e <f> g?

Vâng, tất cả trở nên rất phức tạp. Bây giờ điều tôi không bận tâm là x <y <z tạo ra lỗi cú pháp. Bởi vì tính hữu dụng của nó là nhỏ so với thiệt hại gây ra cho người mới bắt đầu, những người không thể tìm ra x <y <z thực sự làm gì.


4
Vì vậy, trong ngắn hạn, nó chỉ là một tính năng khó để thiết kế tốt.
Jon Purdy

2
Đây không thực sự là một lý do để giải thích tại sao không có ngôn ngữ nổi tiếng nào có tính năng này. Trên thực tế, thật dễ dàng để đưa nó vào một ngôn ngữ theo cách được xác định rõ ràng. Đây chỉ là vấn đề xem nó như một danh sách được kết nối bởi các toán tử cùng loại thay vì mọi toán tử là một nhị phân. Điều tương tự có thể được thực hiện đối với các khoản tiền x + y + z, với sự khác biệt duy nhất không ngụ ý bất kỳ sự khác biệt về ngữ nghĩa. Vì vậy, không có ngôn ngữ nổi tiếng nào từng quan tâm để làm như vậy.
cmaster

1
Tôi nghĩ rằng trong Python đó là một chút tối ưu hóa, ( x < y < ztương đương ((x < y) and (y < z))nhưng ychỉ được đánh giá một lần ) mà tôi tưởng tượng các ngôn ngữ được biên dịch sẽ tối ưu hóa theo cách của chúng. "Bởi vì tính hữu dụng của nó là nhỏ so với thiệt hại gây ra cho người mới bắt đầu, những người không thể tìm ra x <y <z thực sự làm gì." Tôi nghĩ nó cực kỳ hữu ích. Có lẽ sẽ -1 cho điều đó ...
Aaron Hall

Nếu mục tiêu của một người là thiết kế một ngôn ngữ loại bỏ tất cả những thứ có thể gây nhầm lẫn cho những lập trình viên ngu ngốc nhất, thì một ngôn ngữ như vậy đã tồn tại: COBOL. Tôi thà sử dụng python, bản thân tôi, nơi người ta thực sự có thể viết a < b > c < d > e < f > g, với ý nghĩa "rõ ràng" (a < b) and (b > c) and (c < d) and (d > e) and (e < f) and (f > g). Chỉ vì bạn có thể viết không có nghĩa là bạn nên viết. Loại bỏ những điều quái dị như vậy là mục đích của việc xem xét mã. Mặt khác, viết bằng 0 < x < 8python có một dấu hiệu rõ ràng (không có trích dẫn sợ hãi) có nghĩa là x nằm trong khoảng từ 0 đến 8, độc quyền.
David Hammen

@DavidHammen, trớ trêu thay, COBOL thực sự cho phép một <b <c
JoelFan

10

Trong nhiều ngôn ngữ lập trình, x < ylà một biểu thức nhị phân chấp nhận hai toán hạng và ước lượng cho một kết quả boolean duy nhất. Do đó, nếu xâu chuỗi nhiều biểu thức true < zfalse < zsẽ không có ý nghĩa và nếu những biểu thức đó đánh giá thành công, chúng có khả năng tạo ra kết quả sai.

Sẽ dễ dàng hơn nhiều khi nghĩ về x < ymột cuộc gọi hàm có hai tham số và tạo ra một kết quả boolean duy nhất. Trong thực tế, đó là có bao nhiêu ngôn ngữ thực hiện nó dưới mui xe. Nó có thể kết hợp, dễ dàng biên dịch, và nó chỉ hoạt động.

Các x < y < zkịch bản được nhiều phức tạp. Bây giờ, trình biên dịch, thực tế, phải tạo ra ba chức năng : x < y, y < zvà kết quả của hai giá trị đó được kết hợp với nhau, tất cả nằm trong bối cảnh của một ngữ pháp ngôn ngữ mơ hồ có thể tranh cãi .

Tại sao họ làm theo cách khác? Bởi vì nó là ngữ pháp rõ ràng, dễ thực hiện hơn nhiều và dễ dàng hơn để có được chính xác.


2
Nếu bạn đang thiết kế ngôn ngữ, bạn có cơ hội để biến nó thành kết quả đúng .
JesseTG

2
Tất nhiên nó trả lời câu hỏi. Nếu câu hỏi thực sự là tại sao , câu trả lời là "bởi vì đó là những gì các nhà thiết kế ngôn ngữ đã chọn." Nếu bạn có thể đưa ra một câu trả lời tốt hơn thế, hãy tìm nó. Lưu ý rằng về cơ bản Gnasher đã nói chính xác điều tương tự trong đoạn đầu tiên của câu trả lời của mình .
Robert Harvey

3
Một lần nữa, bạn đang chia tóc. Các lập trình viên có xu hướng làm điều đó. "Bạn có muốn bỏ rác không?" "Không." "Bạn sẽ lấy rác ra chứ?" "Vâng."
Robert Harvey

2
Tôi cũng dự thi đoạn cuối. Python hỗ trợ so sánh chuỗi và trình phân tích cú pháp của nó là LL (1). Nó không nhất thiết phải cứng để xác định và thực hiện các ngữ nghĩa hoặc là: Python chỉ nói rằng e1 op1 e2 op2 e3 op3 ...là tương đương với e1 op e2 and e2 op2 e3 and ...ngoại trừ việc mỗi biểu thức chỉ đánh giá một lần. (BTW quy tắc đơn giản này có tác dụng phụ khó hiểu mà các câu lệnh như a == b is Truekhông còn có tác dụng như mong muốn.)

2
@RobertHarvey re:answerĐây là lúc tâm trí tôi ngay lập tức cũng bình luận về câu hỏi chính của tôi. Tôi không xem xét hỗ trợ x < y < zđể thêm bất kỳ giá trị cụ thể nào vào ngữ nghĩa ngôn ngữ. (x < y) && (y < z)được hỗ trợ rộng rãi hơn, bùng nổ hơn, biểu cảm hơn, dễ tiêu hóa hơn trong các thành phần của nó, có thể ghép lại nhiều hơn, logic hơn, dễ dàng tái cấu trúc hơn.
K. Alan Bates

6

Hầu hết các ngôn ngữ chính là (ít nhất một phần) hướng đối tượng. Về cơ bản, nguyên tắc cơ bản của OO là các đối tượng gửi tin nhắn đến các đối tượng khác (hoặc chính họ) và người nhận tin nhắn đó có toàn quyền kiểm soát cách trả lời tin nhắn đó.

Bây giờ, hãy xem cách chúng tôi sẽ thực hiện một cái gì đó như

a < b < c

Chúng tôi có thể đánh giá nó hoàn toàn từ trái sang phải (liên kết trái):

a.__lt__(b).__lt__(c)

Nhưng bây giờ chúng tôi gọi __lt__kết quả của a.__lt__(b), đó là một Boolean. Điều đó không có ý nghĩa.

Hãy thử kết hợp đúng:

a.__lt__(b.__lt__(c))

Không, điều đó cũng không có ý nghĩa. Bây giờ, chúng tôi có a < (something that's a Boolean).

Được rồi, những gì về việc coi nó là đường cú pháp. Hãy tạo một chuỗi <so sánh n gửi tin nhắn n-1-ary. Điều này có thể có nghĩa là, chúng tôi gửi tin nhắn __lt__đến a, gửi bclàm đối số:

a.__lt__(b, c)

Được rồi, nó hoạt động, nhưng có một sự bất cân xứng kỳ lạ ở đây: ađược quyết định liệu nó có ít hơn không b. Nhưng bkhông được quyết định liệu nó có ít hơn hay không c, thay vào đó quyết định đó cũng được đưa ra a.

Điều gì về việc diễn giải nó như một tin nhắn n-ary gửi đến this?

this.__lt__(a, b, c)

Cuối cùng! Điều này có thể làm việc. Tuy nhiên, điều đó có nghĩa là thứ tự của các đối tượng không còn là một thuộc tính của đối tượng (ví dụ: có anhỏ hơn bkhông phải là thuộc tính của ahay không b) mà thay vào đó là một thuộc tính của bối cảnh (nghĩa là this).

Từ một quan điểm chính thống có vẻ kỳ lạ. Tuy nhiên, ví dụ như trong Haskell, điều đó là bình thường. Chẳng hạn, có thể có nhiều cách triển khai khác nhau của kiểu chữ Ord, và việc có anhỏ hơn hay không b, tùy thuộc vào trường hợp kiểu chữ nào xảy ra trong phạm vi.

Nhưng thật ra, nó không gì lạ cả! Cả Java ( Comparator) và .NET ( IComparer) đều có giao diện cho phép bạn đưa quan hệ đặt hàng của riêng bạn vào các thuật toán sắp xếp. Vì vậy, họ hoàn toàn thừa nhận rằng một đơn đặt hàng không phải là thứ được cố định với một loại mà thay vào đó phụ thuộc vào ngữ cảnh.

Theo tôi biết, hiện tại không có ngôn ngữ nào thực hiện bản dịch như vậy. Tuy nhiên, có một ưu tiên: cả IokeSeph đều có cái mà nhà thiết kế của họ gọi là "toán tử nhị phân " - các toán tử có cấu trúc nhị phân, nhưng về mặt ngữ nghĩa . Đặc biệt,

a = b

không hiểu là gửi tin nhắn =để ađi qua bnhư là đối số, nhưng thay vì như gửi thông điệp =đến "Ground hiện tại" (một khái niệm tương tự nhưng không giống hoàn toàn this) đi qua abnhư các đối số. Vì vậy, a = bđược hiểu là

=(a, b)

và không

a =(b)

Điều này có thể dễ dàng được khái quát cho các nhà khai thác n-ary.

Lưu ý rằng điều này thực sự đặc biệt với các ngôn ngữ OO. Trong OO, chúng ta luôn có một đối tượng chịu trách nhiệm chính trong việc diễn giải một tin nhắn gửi, và như chúng ta đã thấy, điều đó không rõ ràng ngay lập tức đối với một thứ giống như a < b < cđối tượng nào nên có.

Điều này không áp dụng cho các ngôn ngữ thủ tục hoặc chức năng mặc dù. Ví dụ, trong Scheme , Common LispClojure , <hàm này là n-ary và có thể được gọi với số lượng đối số tùy ý.

Đặc biệt, <làm không có nghĩa là "ít hơn", chứ không phải các chức năng này được giải thích hơi khác nhau:

(<  a b c d) ; the sequence a, b, c, d is monotonically increasing
(>  a b c d) ; the sequence a, b, c, d is monotonically decreasing
(<= a b c d) ; the sequence a, b, c, d is monotonically non-decreasing
(>= a b c d) ; the sequence a, b, c, d is monotonically non-increasing

3

Điều đó đơn giản là vì các nhà thiết kế ngôn ngữ đã không nghĩ về nó hoặc không nghĩ rằng đó là một ý tưởng tốt. Python thực hiện nó như bạn đã mô tả với một ngữ pháp đơn giản (gần như) LL (1).


1
Điều này vẫn sẽ phân tích cú pháp với một ngữ pháp bình thường trong hầu hết mọi ngôn ngữ chính; nó chỉ không được hiểu chính xác vì lý do @RobertHarvey đưa ra.
Mason Wheeler

@MasonWheeler Không, không nhất thiết. Nếu các quy tắc được viết để các phép so sánh có thể hoán đổi cho nhau với các toán tử khác (giả sử, vì chúng có cùng mức ưu tiên), thì bạn sẽ không có hành vi đúng. Việc Python đưa tất cả các so sánh lên một cấp độ là điều cho phép nó sau đó coi chuỗi là một kết hợp.
Neil G

1
Không hẳn vậy. Đặt 1 < 2 < 3vào Java hoặc C # và bạn không gặp vấn đề với quyền ưu tiên của toán tử; bạn có một vấn đề với các loại không hợp lệ. Vấn đề là điều này vẫn sẽ phân tích chính xác như bạn đã viết, nhưng bạn cần logic trường hợp đặc biệt trong trình biên dịch để biến nó từ một chuỗi các so sánh riêng lẻ thành một so sánh được xâu chuỗi.
Mason Wheeler

2
@MasonWheeler Điều tôi đang nói là ngôn ngữ phải được thiết kế để nó hoạt động. Một phần trong đó là làm cho đúng ngữ pháp. (Nếu các quy tắc được viết sao cho các phép so sánh có thể hoán đổi cho nhau với các toán tử khác, bởi vì chúng có cùng mức ưu tiên, thì bạn sẽ không có hành vi đúng.) không làm Điểm chính trong câu trả lời của tôi là quyết định của nhà thiết kế ngôn ngữ.
Neil G

@MasonWheeler Tôi nghĩ chúng tôi đồng ý. Tôi chỉ nhấn mạnh rằng không khó để hiểu ngữ pháp cho việc này. Đó chỉ là vấn đề quyết định trước rằng bạn muốn nó hoạt động theo cách này.
Neil G

2

Chương trình C ++ sau đây biên dịch với một peep từ clang, ngay cả với các cảnh báo được đặt ở mức cao nhất có thể ( -Weverything):

#include <iostream>
int main () { std::cout << (1 < 3 < 2) << '\n'; }

Mặt khác, bộ biên dịch gnu cảnh báo độc đáo cho tôi điều đó comparisons like 'X<=Y<=Z' do not have their mathematical meaning [-Wparentheses].

Vì vậy, câu hỏi của tôi là: tại sao x <y <z không phổ biến trong các ngôn ngữ lập trình, với ngữ nghĩa dự kiến?

Câu trả lời rất đơn giản: Tương thích ngược. Có một số lượng lớn mã ngoài tự nhiên sử dụng tương đương 1<3<2và mong đợi kết quả là đúng-ish.

Một nhà thiết kế ngôn ngữ có một cơ hội để có được "quyền" này và đó là thời điểm ngôn ngữ được thiết kế lần đầu tiên. Nhận được "sai" ban đầu có nghĩa là các lập trình viên khác sẽ nhanh chóng tận dụng lợi thế của hành vi "sai" đó. Làm cho nó "đúng" lần thứ hai xung quanh sẽ phá vỡ cơ sở mã hiện có.


+1 vì chương trình này xuất ra '1' là kết quả của một biểu thức rõ ràng là sai trong toán học. Mặc dù nó bị chiếm đoạt, một ví dụ trong thế giới thực với các tên biến không thể hiểu sẽ trở thành cơn ác mộng gỡ lỗi nếu tính năng ngôn ngữ này được thêm vào.
Seth Battin

@SethBattin - Đây không phải là cơn ác mộng gỡ lỗi trong Python. Vấn đề duy nhất trong Python là if x == y is True : ..., theo ý kiến ​​của tôi: Những người viết loại mã đó xứng đáng phải chịu một loại tra tấn đặc biệt, đặc biệt mà (nếu anh ta còn sống bây giờ) sẽ khiến Torquemada tự ngất đi.
David Hammen
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.