Dlaczego w Javie / C ++ nie ma operatora mocy?

23

Chociaż istnieje taki operator - **w Pythonie zastanawiałem się, dlaczego Java i C ++ też go nie mają.

Łatwo jest utworzyć jedną dla klas, które definiujesz w C ++ z przeciążeniem operatora (i uważam, że takie rzeczy są możliwe również w Javie), ale mówiąc o prymitywnych typach, takich jak int, double i tak dalej, będziesz musiał użyć biblioteki działają jak Math.power(i zwykle muszą rzutować oba na podwójne).

Dlaczego więc nie zdefiniować takiego operatora dla typów pierwotnych?

RanZilber
źródło
8
W C ++ nie możemy tworzyć własnych operatorów. Możesz przeciążać tylko istniejących operatorów.
1
@Mahesh, więc mogę stworzyć własną klasę liczb i przeciążać ^ operatora, aby być potęgą. To naprawdę nie pasuje.
22
@RanZilber: Ma to znaczenie, ponieważ priorytet ^operatora nie jest zgodny z priorytetem potęgowania. Rozważ wyrażenie a + b ^ c. W matematyce najpierw potęguje się potęgowanie ( b ^ c), a następnie dodaje się wynikową moc a. W C ++ dodawanie odbywa się najpierw ( a + b), a następnie za pomocą ^operatora c. Więc nawet jeśli zaimplementowałeś ^operatora jako potęgowanie, pierwszeństwo zaskoczy wszystkich.
In silico
2
@RamZilber - ^jest XOR w C ++. Zaleca się, aby przeciążony operator nie robił inaczej, niż robi to prymitywny typ danych.
4
@RanZilber: Ponieważ użycie któregokolwiek z operatorów, o których wspomniałeś, oznacza potęgowanie. Poważnie zakwestionowałbym kompetencje każdego programisty C ++, który przeciąża ++operatora lub !operatora et. glin. znaczy potęgowanie. Ale i tak nie możesz, ponieważ operatorzy, o których mówisz, akceptują tylko jeden argument; potęgowanie wymaga dwóch argumentów.
In silico

Odpowiedzi:

32

Mówiąc ogólnie, prymitywne operatory w C (i przez rozszerzenie C ++) są zaprojektowane tak, aby mogły być implementowane przez prosty sprzęt w mniej więcej jednej instrukcji. Coś w rodzaju potęgowania często wymaga wsparcia oprogramowania; więc nie ma go domyślnie.

Ponadto jest dostarczany przez standardową bibliotekę języka w postaci std::pow.

Wreszcie, robienie tego dla typów liczb całkowitych nie miałoby większego sensu, ponieważ większość nawet niewielkich wartości potęgowania wyklucza zakres wymagany dla int, czyli do 65 535. Jasne, możesz to zrobić dla podwójnych i zmiennoprzecinkowych, ale nie ints, ale po co sprawić, że język jest niespójny dla rzadko używanej funkcji?

Billy ONeal
źródło
4
Chociaż zgadzam się z większością tego, fakt, że operator modułu nie może być stosowany na typach zmiennoprzecinkowych, jest niespójny dla prymitywnych typów danych, ale to zbyt prawdopodobne, że nie byłaby to żadna instrukcja na żadnym sprzęcie, który, jak wyobrażam, jest obecnie powszechna.
@Sion: Przynajmniej na x86 moduł jest pojedynczą instrukcją. ( DIVrobi zarówno podział, jak i moduł) Masz mnie jednak w punkcie zgodności.
Billy ONeal
@Billy ONeal: moduł zmiennoprzecinkowy w pojedynczej instrukcji? Do tej pory nie chowałem się na zgromadzeniach, żeby wiedzieć o sobie. W takim przypadku operator modułu powinien mieć zastosowanie do typów zmiennoprzecinkowych.
3
@DonalFellows: FORTRAN miał operatora potęgowania na długo przed czymkolwiek, co przypominało obsługę bignum.
supercat
1
@DonalFellows: Operator mocy nie jest tak przydatny w liczbach całkowitych, jak w przypadku liczb zmiennoprzecinkowych, ale w przypadku małych mocy (szczególnie kwadratowych) z pewnością mógłby mieć swoje zastosowania. Osobiście podoba mi się podejście polegające na tworzeniu operatorów z liter (jak robi to Pascal z divFORTRAN .EQ.); w zależności od reguł białych znaków w języku, może istnieć możliwość posiadania dowolnej liczby operatorów bez wymagania, aby były to słowa zastrzeżone.
supercat
41

Na to pytanie można odpowiedzieć w przypadku C ++: Stroustrup, „Projektowanie i ewolucja C ++” omawia to w sekcji 11.6.1, s. 247–250.

Były ogólne zastrzeżenia do dodania nowego operatora. Dodałoby to do już skomplikowanej tabeli pierwszeństwa. Członkowie grupy roboczej sądzili, że zapewni to jedynie niewielką wygodę w sprawowaniu funkcji, i czasami chcieli móc zastąpić własne funkcje.

Nie było dobrego kandydata na operatora. ^jest wyłącznym-lub, i ^^zaprosił zamieszanie ze względu na związek pomiędzy &a |i &&a ||. !był nieodpowiedni, ponieważ naturalna tendencja do pisania w !=celu potęgowania istniejącej wartości była już zajęta. Być może najlepszy dostępny*^ , które najwyraźniej nikt naprawdę nie lubił.

Stroustrup zastanowił się **jeszcze raz, ale ma już znaczenie w C: a**pjest aczasem, który pwskazuje, i char ** c;deklaruje cjako wskaźnik do wskaźnika char. Wprowadzenie **jako tokena oznaczającego „deklarację wskaźnika do wskaźnika”, „razy”, co wskazuje następna rzecz „(jeśli jest to wskaźnik) lub„ wykładnik ”(jeśli następuje po nim liczba), spowodowało problemy z pierwszeństwem. a/b**pmusiałby parsować tak, a/(b**p)jakby p był liczbą, ale (a/b) * *pgdyby p był wskaźnikiem, to musiałoby to zostać rozwiązane w parserze.

Innymi słowy, byłoby to możliwe, ale skomplikowałoby to tabelę pierwszeństwa i parser, a oba są już zbyt skomplikowane.

Nie znam historii o Javie; wszystko, co mogłem zrobić, to spekulować. Jeśli chodzi o C, w którym się zaczął, wszystkie operatory C są łatwo tłumaczone na kod asemblera, częściowo w celu uproszczenia kompilatora, a częściowo w celu uniknięcia ukrywania czasochłonnej funkcjonalności w prostych operatorach (fakt, że operator+()i inni mogli ukryć wielką złożoność i wydajności wydajności był jednym z nich wczesnych skarg na C ++).

David Thornley
źródło
2
Niezła odpowiedź. Podejrzewam, że Java próbowała uprościć C pod tym względem, więc nikt nie chciał dodawać nowego operatora. Szkoda, że ​​nikt mnie nie pytał, na pewno bym tego chciał *^. : D
maaartinus,
1
C został zbudowany do formatowania tekstu. Fortran został zbudowany do matematyki i miał 20 lat wcześniej matematykę złożoną, matematyczną i matematyczną.
Martin Beckett
@Martin Beckett: Czy możesz znaleźć dowody na to, że C został zbudowany do formatowania tekstu? Wydaje mi się to bardzo niezdarnym językiem, a to, co przeczytałem o pochodzeniu C, mówi, że został on zaprojektowany przede wszystkim do programowania systemu dla Uniksa.
David Thornley
@DavidThornley - Został zaprojektowany do pisania w Uniksie, ale wydaje się, że wszystkie wczesne zastosowania Uniksa polegały na formatowaniu tekstu, a na ten czas ma obszerną bibliotekę napisów i we / wy.
Martin Beckett
6
+1: Istniejące znaczenie słowa a**p„zabójca”. (Włamania do obejścia tego problemu… Brr!)
Donal Fellows
13

I podejrzewam, że to dlatego, że każdy operator przedstawisz zwiększa złożoność języka. Bariera wejścia jest zatem bardzo wysoka. Używam potęgowania bardzo, bardzo rzadko - i cieszę się, że mogę to zrobić za pomocą wywołania metody.

Jon Skeet
źródło
3
Używałbym x**2i x**3nie tak rzadko. Przydałaby się magiczna implementacja pow, o której kompilator wie i optymalizuje dla prostych przypadków.
CodesInChaos
2
@CodeInChaos: Jednak x * xi x * x * xnie są złym zamiennikiem kwadratu i sześcianu.
David Thornley,
5
@ David nie możesz po prostu pisać, x*xjeśli x jest wyrażeniem. W najlepszym przypadku kod staje się nieporęczny, aw najgorszym wolniejszy lub nawet błędny. Musisz więc zdefiniować własne funkcje Square i Cube. I nawet wtedy kod byłby brzydszy niż użycie ** jako operatora mocy.
CodesInChaos
1
@David Muszę wstawić nawiasy tak, ale nie muszę powtarzać tego wyrażenia kilka razy i powoduje powiększenie kodu źródłowego. Co znacznie zmniejsza czytelność. A powszechna eliminacja podwyrażeń jest możliwa tylko wtedy, gdy kompilator może zagwarantować, że wyrażenie jest wolne od skutków ubocznych. I przynajmniej jitter .net nie jest pod tym względem zbyt mądry.
CodesInChaos
11

Projektanci języka Java i głównych bibliotek postanowili przenieść większość operacji matematycznych do klasy Math . Zobacz Math.pow () .

Czemu? Elastyczność w celu nadania priorytetu wydajności w porównaniu z precyzją bit-za-bit. Stwierdzenie, że zachowanie wbudowanych operatorów matematycznych może się różnić w zależności od platformy, byłoby sprzeczne z resztą specyfikacji językowej, podczas gdy klasa Math wyraźnie stwierdza, że ​​zachowanie potencjalnie poświęca precyzję wydajności, dlatego nabywca powinien się wystrzegać:

W przeciwieństwie do niektórych metod numerycznych klasy StrictMath , wszystkie implementacje równoważnych funkcji klasy Math nie są zdefiniowane tak, aby zwracały te same wyniki bit po bicie. Ten relaks pozwala na bardziej wydajne wdrożenia, w których nie jest wymagana ścisła odtwarzalność.


źródło
6

Potęgowanie było częścią Fortran od samego początku, ponieważ było ukierunkowane wprost na programowanie naukowe. Inżynierowie i fizycy często używają go w symulacjach, ponieważ związki prawa mocy są powszechne w fizyce.

Python jest również mocno obecny w informatyce naukowej (np. NumPy i SciPy). To, wraz z operatorem potęgowania, sugeruje, że miał on również na celu programowanie naukowe.

C, Java i C # mają swoje korzenie w programowaniu systemu. Być może jest to wpływ, który utrzymywał potęgowanie poza grupą obsługiwanych operatorów.

Po prostu teoria.

duffymo
źródło
4

C zdefiniował tylko operatory dla typowych operacji arytmetycznych dostępnych z ALU. Jego głównym celem było stworzenie czytelnego dla człowieka interfejsu do kodu asemblera.

C ++ nie zmienił żadnego zachowania operatora, ponieważ chciał, aby cała baza kodu napisana w C była zgodna.

Java zrobiła to samo, ponieważ nie chciała zastraszyć istniejących programistów C ++.

Tugrul Ates
źródło
Kiedy stworzono C, mnożenie i dzielenie nierzadko brakowało sprzętu i musiało być zaimplementowane w oprogramowaniu. Jednak C ma operatory mnożenia i dzielenia.
siride,
@siride: O ile mi wiadomo, PDP-7, pierwszy komputer z systemem Unix, miał multiplikację sprzętową i podział przez EAE. Zobacz: bitsavers.org/pdf/dec/pdp7/F-75_PDP-7userHbk_Jun65.pdf
Tugrul Ates
1

Cóż, ponieważ każdy operator, który miałby sens dla mocy, jest już w użyciu. ^ oznacza XOR, a ** definiuje wskaźnik do wskaźnika. Zamiast tego mają po prostu funkcję, która robi to samo. (jak pow ())

Skyler Saleh
źródło
@RTS - Czy programista langauge naprawdę szuka sensu więcej niż skuteczności?
Dobry programista języka programowania patrzy na oba. Nie mogę nic powiedzieć o Javie. Ale w c ++ funkcja pow () jest obliczana w czasie kompilacji. I jest tak samo wydajny jak zwykli operatorzy.
@RTS: pow()Funkcja wykonuje swoje obliczenia w czasie wykonywania, chyba że masz kompilator, który może wykonywać ciągłe składanie pow(), o co bardzo wątpię. (Niektóre kompilatory dają jednak możliwość użycia wewnętrznych funkcji procesora do wykonania obliczeń.)
In silico
@In silico nie miałem na myśli, że oblicza ostateczną wartość, miałem na myśli, że kompilatory zoptymalizują wywołanie funkcji, więc masz po prostu surowe równanie.
2
@josefx: Pewnie to dobry powód. Pojedynczy *to token leksykalny, bez względu na to, czy jest używany do pośredniego czy mnożenia. **Sens potęgowanie będzie jeden lub dwa tokeny leksykalne, a ty naprawdę nie chcesz lexer musiał uderzyć tablicę symboli do tokenize.
David Thornley,
0

Faktem jest, że operatory arytmetyczne są jedynie skrótami funkcji. (Prawie) Wszystko, co z nimi zrobisz, można wykonać za pomocą funkcji. Przykład:

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

Jest to po prostu bardziej szczegółowe, więc nie widzę nic złego w używaniu funkcji do wykonywania „mocy”.

Xeo
źródło
-1

Dodawanie / odejmowanie / negowanie i mnożenie / dzielenie to podstawowe operatory matematyczne. Gdybyś miał uczynić moc operatorem, gdzie byś przestał? Operator pierwiastka kwadratowego? Operator N-root? Operator logarytmiczny?

Nie mogę mówić za ich twórcami, ale mogę powiedzieć, że myślę, że takie operatory w języku stałyby się niewygodne, a nie ortogonalne. Pozostała na klawiaturze liczba znaków alfanumerycznych / białych znaków jest raczej ograniczona. W tej chwili dziwne jest, że w C ++ jest operator modułu.

Sion Sheevok
źródło
+1 - Nie rozumiem, dlaczego posiadanie modjako operatora jest dziwne. Zwykle jest to pojedyncza instrukcja. Jest to operacja pierwotna na liczbach całkowitych. Najczęściej stosuje się go w informatyce. (Wdrażanie rzeczy takich jak ograniczone bufory bez modśmierdziałoby)
Billy ONeal
@Billy ONeal: Dziwne z powodu niespójności między możliwością korzystania z typów całkowitych i typów zmiennoprzecinkowych. Jest to absolutnie przydatne i nie marzę o jego usunięciu. Dziwne to wszystko.