Tại sao không có toán tử nguồn trong Java / C ++?


23

Trong khi có toán tử như vậy - **trong Python, tôi đã tự hỏi tại sao Java và C ++ không có cái đó.

Thật dễ dàng để tạo một lớp cho các lớp bạn định nghĩa trong C ++ với quá tải toán tử (và tôi tin điều đó cũng có thể có trong Java), nhưng khi nói về các kiểu nguyên thủy như int, double, v.v., bạn sẽ phải sử dụng thư viện chức năng như Math.power(và thường phải đúc cả hai để tăng gấp đôi).

Vậy - tại sao không định nghĩa một toán tử như vậy cho các kiểu nguyên thủy?


8
Trong C ++, chúng ta không thể tạo toán tử riêng. Bạn chỉ có thể quá tải các toán tử hiện có.

1
@Mahesh, vì vậy tôi có thể tạo lớp Number của riêng mình và toán tử ^ quá tải để trở thành một quyền lực. Điều đó thực sự không mather.

22
@RanZilber: Không thành vấn đề vì mức độ ưu tiên của ^toán tử không khớp với độ ưu tiên của lũy thừa. Hãy xem xét biểu thức a + b ^ c. Trong toán học, phép lũy thừa được thực hiện trước tiên ( b ^ c), sau đó công suất kết quả được thêm vào a. Trong C ++, phép cộng được thực hiện trước ( a + b) sau đó ^toán tử được thực hiện với c. Vì vậy, ngay cả khi bạn đã thực hiện ^toán tử có nghĩa là lũy thừa, thì quyền ưu tiên sẽ làm mọi người ngạc nhiên.
Trong silico

2
@RamZilber - ^là một XOR trong C ++. Chúng tôi khuyên rằng toán tử quá tải không nên làm gì khác với kiểu dữ liệu nguyên thủy sử dụng nó.

4
@RanZilber: Bởi vì hoàn toàn không trực quan khi sử dụng bất kỳ toán tử nào bạn đề cập có nghĩa là lũy thừa. Tôi nghiêm túc đặt câu hỏi về năng lực của bất kỳ lập trình viên C ++ nào làm quá tải ++toán tử hoặc !toán tử et. al. có nghĩa là lũy thừa. Nhưng dù sao bạn cũng không thể, bởi vì các toán tử mà bạn nói về chỉ chấp nhận một đối số; lũy thừa đòi hỏi hai đối số.
Trong silico ngày

Câu trả lời:


32

Nói chung, các toán tử nguyên thủy trong C (và bởi phần mở rộng C ++) được thiết kế để có thể thực hiện được bằng phần cứng đơn giản trong khoảng một lệnh. Một cái gì đó như lũy thừa thường yêu cầu hỗ trợ phần mềm; vì vậy nó không có ở đó theo mặc định.

Ngoài ra, nó được cung cấp bởi thư viện tiêu chuẩn của ngôn ngữ ở dạng std::pow.

Cuối cùng, làm điều này cho các kiểu dữ liệu số nguyên sẽ không có nhiều ý nghĩa, bởi vì hầu hết các giá trị nhỏ cho phép lũy thừa thổi ra phạm vi cần thiết cho int, lên tới 65.535. Chắc chắn, bạn có thể làm điều này cho đôi và nổi nhưng không phải ints, nhưng tại sao làm cho ngôn ngữ không nhất quán cho một tính năng hiếm khi được sử dụng?


4
Mặc dù tôi đồng ý với hầu hết điều này, thực tế là toán tử mô đun không thể được sử dụng trên các loại dấu phẩy động không phù hợp với các kiểu dữ liệu nguyên thủy, nhưng điều đó có lẽ sẽ không phải là một hướng dẫn duy nhất trên bất kỳ phần cứng nào mà tôi tưởng tượng là hiện nay.

@Sion: Ít nhất là trên x86, modulus là một lệnh đơn. ( DIVcả phân chia và mô đun) Mặc dù vậy, bạn đã cho tôi về điểm nhất quán.
Billy ONeal

@Billy ONeal: Mô đun điểm nổi trong một lệnh đơn? Tôi đã không lúng túng trong hội nghị vào lúc muộn để biết về bản thân mình. Nếu đó là trường hợp, thì toán tử mô đun nên được áp dụng cho các loại dấu phẩy động.

3
@DonalFellows: FORTRAN đã có toán tử lũy thừa từ lâu trước khi nó có bất cứ thứ gì tương tự như hỗ trợ bignum.
supercat

1
@DonalFellows: Một toán tử năng lượng không hữu dụng với các số nguyên như với số float, nhưng đối với các lũy thừa nhỏ (đặc biệt là bình phương) thì nó chắc chắn có thể có công dụng của nó. Cá nhân tôi thích cách tiếp cận làm cho các toán tử thoát khỏi các chữ cái (như Pascal làm với divhoặc FORTRAN với .EQ.); tùy thuộc vào quy tắc khoảng trắng ngôn ngữ, có thể có số lượng toán tử tùy ý mà không yêu cầu chúng phải là từ dành riêng.
supercat

41

Câu hỏi này có thể trả lời được cho C ++: Stroustrup, "Thiết kế và tiến hóa của C ++" thảo luận về vấn đề này trong phần 11.6.1, trang 247-250.

Có sự phản đối chung để thêm một toán tử mới. Nó sẽ thêm vào bảng ưu tiên đã quá phức tạp. Các thành viên của nhóm làm việc nghĩ rằng nó sẽ chỉ mang lại sự thuận tiện nhỏ khi có chức năng và đôi khi họ muốn có thể thay thế chức năng của mình.

Không có ứng cử viên tốt cho một nhà điều hành. ^là độc quyền-hoặc, và ^^sự nhầm lẫn mời vì mối quan hệ giữa &|&&||. !là không phù hợp vì sẽ có xu hướng tự nhiên là viết !=theo cấp số nhân của một giá trị hiện có, và điều đó đã được thực hiện. Điều tốt nhất có thể có được *^, mà dường như không ai thực sự thích.

Stroustrup đã xem xét **lại, nhưng nó đã có một ý nghĩa trong C: a**palần bất cứ điều gì ptrỏ đến và char ** c;tuyên bố clà một con trỏ tới con trỏ tới char. Giới thiệu **dưới dạng mã thông báo có nghĩa là "khai báo một con trỏ tới con trỏ tới", "nhân với những gì điều tiếp theo trỏ đến" (nếu đó là một con trỏ) hoặc "lũy thừa" (nếu theo sau là một số) gây ra các vấn đề ưu tiên. a/b**psẽ phải phân tích cú pháp như a/(b**p)thể p là một số, nhưng (a/b) * *pnếu p là một con trỏ, thì điều này sẽ phải được giải quyết trong trình phân tích cú pháp.

Nói cách khác, điều đó là có thể, nhưng nó sẽ làm phức tạp bảng ưu tiên và trình phân tích cú pháp, và cả hai đều quá phức tạp.

Tôi không biết câu chuyện về Java; tất cả những gì tôi có thể làm sẽ là suy đoán. Đối với C, nơi nó bắt đầu, tất cả các toán tử C đều dễ dàng được dịch thành mã lắp ráp, một phần để đơn giản hóa trình biên dịch và một phần để tránh ẩn chức năng tiêu tốn thời gian trong các toán tử đơn giản (thực tế là các toán tử operator+()khác có thể che giấu sự phức tạp và hiệu năng rất lớn là một của những khiếu nại sớm về C ++).


2
Câu trả lời tốt đẹp. Tôi đoán Java đã cố gắng đơn giản hóa C về mặt này, vì vậy không ai muốn thêm một toán tử mới. Đó là một điều đáng tiếc mà không ai hỏi tôi, tôi chắc chắn sẽ thích *^. : D
maaartinus

1
C được xây dựng để làm định dạng văn bản. Fortran được xây dựng để làm toán và có các phép toán phức tạp, sức mạnh và ma trận 20 năm trước.
Martin Beckett

@Martin Beckett: Bạn có thể tìm thấy bằng chứng cho thấy C được xây dựng để định dạng văn bản không? Nó dường như là một ngôn ngữ rất vụng về cho điều đó, và những gì tôi đã đọc về nguồn gốc của C nói rằng nó được thiết kế để lập trình hệ thống cho Unix là chủ yếu.
David Thornley

@DavidThornley - Nó được thiết kế để viết Unix, nhưng tất cả các sử dụng Unix ban đầu dường như là định dạng văn bản và đến lúc này nó có một thư viện chuỗi và thư viện mở rộng.
Martin Beckett

6
+1: Ý nghĩa hiện tại của a**plà kẻ giết người. (Các bản hack để giải quyết vấn đề đó, Brr Brr!)
Donal Fellows

13

Tôi nghi ngờ đó là bởi vì mọi nhà khai thác bạn giới thiệu làm tăng sự phức tạp của ngôn ngữ. Do đó, rào cản gia nhập rất cao. Tôi thấy mình sử dụng lũy ​​thừa rất, rất hiếm khi - và tôi rất vui khi sử dụng một cuộc gọi phương thức để làm như vậy.



3
Tôi sẽ sử dụng x**2x**3không hiếm khi. Và một triển khai pow ma thuật mà trình biên dịch biết và tối ưu hóa cho các trường hợp đơn giản sẽ là tốt đẹp.
CodeInChaos

2
@CodeInChaos: Tuy nhiên x * xx * x * xkhông thay thế xấu cho hình vuông và hình khối.
David Thornley

5
@David bạn không thể đơn giản viết x*xnếu x là một biểu thức. Trong trường hợp tốt nhất, mã trở nên khó sử dụng và trong trường hợp xấu nhất chậm hơn hoặc thậm chí sai. Vì vậy, bạn cần xác định các hàm Square và Cube của riêng mình. Và thậm chí sau đó mã sẽ xấu hơn so với sử dụng ** làm toán tử điện.
CodeInChaos

1
@David Tôi cần đặt dấu ngoặc đơn có, nhưng không cần lặp lại biểu thức nhiều lần và làm mờ mã nguồn. Mà làm giảm khả năng đọc rất nhiều. Và loại bỏ phổ biến phụ chỉ có thể nếu trình biên dịch có thể đảm bảo biểu thức không có tác dụng phụ. Và ít nhất, jitter .net không quá thông minh trong vấn đề đó.
CodeInChaos

11

Các nhà thiết kế thư viện lõi và ngôn ngữ Java đã quyết định giao hầu hết các hoạt động toán học cho lớp Toán . Xem Math.pow () .

Tại sao? Tính linh hoạt để ưu tiên hiệu suất hơn độ chính xác bit-bit. Nó sẽ đi ngược lại với phần còn lại của thông số ngôn ngữ để nói rằng hành vi của các toán tử tích hợp có thể thay đổi từ nền tảng này sang nền tảng khác, trong khi lớp Math đặc biệt nói rằng hành vi có khả năng hy sinh độ chính xác cho hiệu suất, vì vậy người mua hãy cẩn thận:

Không giống như một số phương thức số của lớp StrictMath , tất cả các cài đặt của các hàm tương đương của lớp Toán không được xác định để trả về các kết quả tương tự bit-bit-bit. Sự thư giãn này cho phép thực hiện tốt hơn khi không yêu cầu tái sản xuất nghiêm ngặt.


6

Mở rộng lũy ​​thừa là một phần của Fortran ngay từ đầu vì nó được nhắm thẳng vào chương trình khoa học. Các kỹ sư và nhà vật lý sử dụng nó thường xuyên trong các mô phỏng, bởi vì các mối quan hệ pháp luật sức mạnh là phổ biến trong vật lý.

Python cũng có sự hiện diện mạnh mẽ trong điện toán khoa học (ví dụ NumPy và SciPy). Điều đó, cùng với toán tử lũy thừa của nó, cho thấy rằng nó cũng nhằm mục đích lập trình khoa học.

C, Java và C # có nguồn gốc từ lập trình hệ thống. Có lẽ đó là một ảnh hưởng giúp loại bỏ lũy thừa ra khỏi nhóm các nhà khai thác được hỗ trợ.

Chỉ là một lý thuyết.


4

C chỉ xác định toán tử cho các phép toán số học phổ biến có thể truy cập được với ALU. Mục đích chính của nó là tạo ra một giao diện dễ đọc với mã hội.

C ++ không thay đổi bất kỳ hành vi vận hành nào bởi vì, nó muốn tất cả các cơ sở mã được viết bằng C phải tuân thủ.

Java cũng làm như vậy vì nó không muốn đe dọa các lập trình viên C ++ hiện có.


Khi C được tạo, phép nhân và chia không thường xuyên thiếu phần cứng và phải được thực hiện trong phần mềm. Tuy nhiên, C có các toán tử nhân và chia.
siride

@siride: Theo hiểu biết của tôi, PDP-7 là máy tính đầu tiên chạy Unix, đã nhân và chia phần cứng thông qua EAE của nó. Vui lòng xem: bitsavers.org/pdf/dec/pdp7/F-75_PDP-7userHbk_Jun65.pdf
Tugrul Ates

1

Vâng, bởi vì mọi nhà khai thác sẽ có ý nghĩa cho một sức mạnh đã được sử dụng. ^ là XOR và ** định nghĩa một con trỏ tới một con trỏ. Vì vậy, thay vào đó họ chỉ có một chức năng làm điều tương tự. (như pow ())


@RTS - Có phải một nhà phát triển langauge thực sự đang tìm kiếm ý nghĩa nhiều hơn hiệu quả?

Một nhà phát triển tốt của một ngôn ngữ lập trình nhìn vào cả hai. Tôi không thể nói bất cứ điều gì về java. Nhưng trong c ++, hàm pow () được tính toán tại thời gian biên dịch. Và cũng hiệu quả như các nhà khai thác thông thường.

@RTS: pow()Hàm thực hiện tính toán của nó trong thời gian chạy, trừ khi bạn có một trình biên dịch có thể thực hiện gấp liên tục pow(), điều mà tôi rất nghi ngờ. (Tuy nhiên, một số trình biên dịch cung cấp cho bạn tùy chọn sử dụng nội tại của bộ xử lý để thực hiện phép tính.)
Trong silico

@ Trong silico Tôi không có nghĩa là nó tính giá trị cuối cùng, ý tôi là trình biên dịch sẽ tối ưu hóa cuộc gọi hàm, vì vậy bạn chỉ cần có phương trình thô.

2
@josefx: Chắc chắn đó là một lý do tốt. Một đơn *là một mã thông báo từ vựng, cho dù nó được sử dụng cho mục đích hoặc nhân. Một **phép lũy thừa có nghĩa là một hoặc hai mã thông báo từ vựng và bạn thực sự không muốn từ vựng của mình phải nhấn vào bảng biểu tượng để token hóa.
David Thornley

0

Thực tế là, toán tử số học chỉ là các phím tắt của hàm. (Gần như) Mọi thứ bạn làm với chúng đều có thể được thực hiện với một hàm. Thí dụ:

c = a + b;
// equals
c.set(a.add(b));
// or as free functions
set(c, add(a,b));

Nó chỉ dài dòng hơn, vì vậy tôi thấy không có gì sai khi sử dụng các hàm để thực hiện 'sức mạnh của'.


-1

Phép cộng / phép trừ / phủ định và phép nhân / phép chia là các toán tử cơ bản. Nếu bạn định tạo ra một nhà điều hành, bạn sẽ dừng ở đâu? Toán tử căn bậc hai? Toán tử gốc N? Toán tử logarit?

Tôi không thể nói cho những người sáng tạo của họ, nhưng tôi có thể nói rằng tôi nghĩ nó trở nên khó sử dụng và không trực giao khi có các toán tử như vậy trong ngôn ngữ. Số lượng ký tự không phải là số-alpha / khoảng trắng còn lại trên bàn phím khá hạn chế. Vì nó là lạ, có một toán tử mô đun trong C ++.


+1 - Tôi không thấy lý do tại sao có modmột nhà điều hành là lạ. Nó thường là một hướng dẫn duy nhất. Đây là một hoạt động nguyên thủy trên số nguyên. Nó được sử dụng hầu hết mọi nơi trong khoa học máy tính. (Thực hiện những thứ như bộ đệm bị ràng buộc mà không modcó mùi hôi thối)
Billy ONeal

@Billy ONeal: Lạ vì sự không nhất quán giữa việc có thể sử dụng với loại số nguyên và loại dấu phẩy động. Hoàn toàn hữu ích mặc dù và tôi sẽ không mơ về việc loại bỏ nó. Chỉ cần kỳ quặc là tất cả.
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.