Perché non esiste un operatore di potenza in Java / C ++?


23

Mentre esiste un tale operatore - **in Python, mi chiedevo perché anche Java e C ++ non ne abbiano uno.

È facile crearne uno per le classi definite in C ++ con sovraccarico dell'operatore (e credo che ciò sia possibile anche in Java), ma quando si parla di tipi primitivi come int, double e così via, è necessario utilizzare la libreria funziona come Math.power(e di solito deve lanciare entrambi per raddoppiare).

Quindi - perché non definire un tale operatore per i tipi primitivi?


8
In C ++, non possiamo creare i nostri operatori. È possibile solo sovraccaricare gli operatori esistenti.

1
@Mahesh, quindi posso creare la mia classe Number e l'operatore overload ^ per essere un potere. Che davvero non raccoglie.

22
@RanZilber: è importante perché la precedenza ^dell'operatore non corrisponde alla precedenza dell'espiazione. Considera l'espressione a + b ^ c. In matematica, l'esponenziazione viene eseguita per prima ( b ^ c), quindi viene aggiunta la potenza risultante a. In C ++, l'aggiunta viene eseguita per prima (a + b ) quindi l' ^operatore viene eseguito con c. Quindi, anche se hai implementato l' ^operatore per indicare l'espiazione, la precedenza sorprenderà tutti.
In silico,

2
@RamZilber - ^è un XOR in C ++. Si consiglia che l'operatore sovraccarico non dovrebbe fare diversamente ciò che fa un tipo di dati primitivo che lo utilizza.

4
@RanZilber: Perché non è affatto intuitivo utilizzare uno di quegli operatori che menzioni per significare esponenziazione. Vorrei seriamente mettere in discussione la competenza di qualsiasi programmatore C ++ che sovraccarica l' ++operatore o l' !operatore et. al. per significare esponenziale. Ma non puoi comunque, perché gli operatori di cui parli accettano solo un argomento; l'espiazione richiede due argomenti.
In silico,

Risposte:


32

In generale, gli operatori primitivi in ​​C (e per estensione C ++) sono progettati per essere implementabili da un semplice hardware in circa una singola istruzione. Qualcosa di simile all'esponenziazione richiede spesso supporto software; quindi non è lì per impostazione predefinita.

Inoltre, è fornito dalla libreria standard della lingua sotto forma di std::pow.

Infine, fare questo per i tipi di dati interi non avrebbe molto senso, perché la maggior parte anche dei valori piccoli per esponenziazione eliminano l'intervallo richiesto per int, che è fino a 65.535. Certo, potresti farlo per doppi e float ma non ints, ma perché rendere il linguaggio incoerente per una funzione usata raramente?


4
Sebbene io sia d'accordo con la maggior parte di questo, il fatto che l'operatore del modulo non possa essere utilizzato su tipi a virgola mobile è incoerente per i tipi di dati primitivi, ma che troppo probabilmente non sarebbe una singola istruzione su qualsiasi hardware che immagino sia oggi prevalente.

@Sion: almeno su x86, il modulo è una singola istruzione. ( DIVfa sia divisione che modulo) Mi hai preso sul punto di coerenza però.
Billy ONeal

@Billy ONeal: modulo in virgola mobile in una singola istruzione? Non mi sono dilungato in assemblea fino a tardi per sapere da solo. In tal caso, l'operatore del modulo dovrebbe essere reso applicabile ai tipi a virgola mobile.

3
@DonalFellows: FORTRAN aveva l'operatore esponenziale molto prima che avesse qualcosa di simile al supporto del bignum.
supercat,

1
@DonalFellows: un operatore di potenza non è così utile con numeri interi come con float, ma per piccoli poteri (in particolare la quadratura) potrebbe sicuramente avere i suoi usi. Personalmente mi piace l'approccio di trasformare gli operatori in lettere (come Pascal con divo FORTRAN con .EQ.); a seconda delle regole degli spazi bianchi nella lingua, può essere possibile avere un numero arbitrario di operatori senza richiedere che siano parole riservate.
supercat

41

Questa domanda risponde a C ++: Stroustrup, "Design and Evolution of C ++" ne discute nella sezione 11.6.1, pagg. 247-250.

Vi erano obiezioni generali all'aggiunta di un nuovo operatore. Si aggiungerebbe alla tabella di precedenza già troppo complicata. I membri del gruppo di lavoro pensavano che avrebbe avuto solo una piccola comodità sull'avere una funzione e volevano poter sostituire le proprie funzioni a volte.

Non c'era un buon candidato per un operatore. ^è ^^confusione esclusiva o invitata a causa della relazione tra &e |e &&e ||. !era inadatto dato che ci sarebbe stata la naturale tendenza a scrivere !=per esponenziare un valore esistente, e questo era già stato preso. Potrebbe essere stato il migliore disponibile *^, che apparentemente non è piaciuto a nessuno.

Stroustrup ripensò **, ma ha già un significato in C: a**pè avolte qualunque cosa ppunti e char ** c;dichiara ccome un puntatore a cui puntare char. L'introduzione **come token che significa "dichiarazione di un puntatore a puntatore a", "volte ciò a cui punta la cosa successiva" (se è un puntatore) o "esponenziazione" (se seguita da un numero) ha causato problemi di precedenza. a/b**pdovrebbe analizzare come a/(b**p)se p fosse un numero, ma (a/b) * *pse p fosse un puntatore, quindi questo dovrebbe essere risolto nel parser.

In altre parole, sarebbe stato possibile, ma avrebbe complicato la tabella di precedenza e il parser, ed entrambi sono già troppo complicati.

Non conosco la storia di Java; tutto ciò che potrei fare sarebbe speculare. Per quanto riguarda C, da dove è iniziato, tutti gli operatori C sono facilmente tradotti in codice assembly, in parte per semplificare il compilatore e in parte per evitare di nascondere funzionalità dispendiose in termini di tempo in operatori semplici (il fatto che operator+()e altri possano nascondere grande complessità e risultati di prestazioni era uno delle prime lamentele sul C ++).


2
Bella risposta. Immagino che Java abbia cercato di semplificare C in questo senso, quindi nessuno voleva aggiungere un nuovo operatore. È un peccato che nessuno mi abbia chiesto, sicuramente mi sarebbe piaciuto *^. : D
maaartinus,

1
C è stato creato per eseguire la formattazione del testo. Fortran fu costruito per fare matematica e 20 anni prima aveva matematica complessa, di potenza e di matrice.
Martin Beckett,

@Martin Beckett: Riesci a trovare prove che C è stato creato per la formattazione del testo? Mi sembra un linguaggio molto goffo per quello, e quello che ho letto sull'origine di C dice che è stato progettato principalmente per la programmazione del sistema per Unix.
David Thornley,

@DavidThornley - È stato progettato per scrivere in Unix, ma tutti i primi usi di Unix sembrano essere stati la formattazione del testo, e per il momento ha una stringa estesa e una libreria di I / O.
Martin Beckett,

6
+1: Il significato esistente per a**pè l'assassino. (Gli hack per aggirare quel problema ... Brr!)
Donal Fellows il

13

Ho il sospetto che sia perché ogni operatore che introduci aumenta la complessità della lingua. La barriera per l'ingresso è quindi molto alta. Mi ritrovo a usare l'esponenziazione molto, molto raramente - e sono più che felice di usare una chiamata di metodo per farlo.



3
Userei x**2e x**3non così di rado. E sarebbe gradita un'implementazione di pow magica che il compilatore conosce e ottimizza per i casi semplici.
Codici InChaos

2
@CodeInChaos: Tuttavia x * xe x * x * xnon sono sostituti sbagliati per il quadrato e il cubo.
David Thornley

5
@David non puoi semplicemente scrivere x*xse x è un'espressione. Nel migliore dei casi il codice diventa ingombrante e, nel peggiore dei casi, più lento o addirittura sbagliato. Quindi dovresti definire le tue funzioni Square e Cube. E anche allora il codice sarebbe più brutto che usare ** come operatore di potenza.
Codici InChaos

1
@David Ho bisogno di mettere le parentesi sì, ma non ho bisogno di ripetere più volte l'espressione e gonfiare il codice sorgente. Ciò riduce molto la leggibilità. E l'eliminazione della sottoespressione comune è possibile solo se il compilatore può garantire che l'espressione sia priva di effetti collaterali. E almeno .net jitter non è troppo intelligente in questo senso.
Codici InChaos

11

I progettisti del linguaggio Java e delle librerie core hanno deciso di relegare la maggior parte delle operazioni matematiche alla classe di matematica . Vedi Math.pow () .

Perché? Flessibilità per stabilire le priorità delle prestazioni rispetto alla precisione bit per bit. Sarebbe contrario al resto delle specifiche del linguaggio affermare che il comportamento degli operatori matematici integrati potrebbe variare da piattaforma a piattaforma, mentre la classe Math afferma specificamente che il comportamento sacrifica potenzialmente la precisione per le prestazioni, quindi l'acquirente deve fare attenzione:

A differenza di alcuni dei metodi numerici della classe StrictMath , tutte le implementazioni delle funzioni equivalenti della classe Math non sono definite per restituire gli stessi risultati bit per bit. Questo rilassamento consente implementazioni più performanti dove non è richiesta una rigida riproducibilità.


6

L'esponenziazione faceva parte di Fortran sin dall'inizio perché mirava direttamente alla programmazione scientifica. Ingegneri e fisici lo usano spesso nelle simulazioni, perché le relazioni di potere sono comuni in fisica.

Python ha una forte presenza anche nel calcolo scientifico (ad esempio NumPy e SciPy). Ciò, insieme al suo operatore di esponenziazione, suggerisce che fosse rivolto anche alla programmazione scientifica.

C, Java e C # hanno radici nella programmazione del sistema. Forse questa è un'influenza che ha impedito l'espiazione dal gruppo di operatori supportati.

Solo una teoria.


4

C ha definito solo operatori per operazioni aritmetiche comuni accessibili con la ALU. Il suo obiettivo principale era quello di creare un'interfaccia umana leggibile al codice Assembly.

C ++ non ha modificato alcun comportamento dell'operatore perché voleva che tutta la base di codice scritta in C fosse conforme.

Java ha fatto lo stesso perché non voleva intimidire i programmatori C ++ esistenti.


Quando è stato creato il C, la moltiplicazione e la divisione non erano raramente carenti di hardware e dovevano essere implementate nel software. Eppure C ha operatori di moltiplicazione e divisione.
siride,

@siride: Per quanto ne sappia, il PDP-7, il primo computer con Unix, presentava una moltiplicazione e una divisione dell'hardware attraverso il suo EAE. Si prega di consultare: bitsavers.org/pdf/dec/pdp7/F-75_PDP-7userHbk_Jun65.pdf
Tugrul Ates l'

1

Bene, perché ogni operatore che avrebbe senso per una potenza è già in uso. ^ è XOR e ** definisce un puntatore a un puntatore. Quindi invece hanno solo una funzione che fa la stessa cosa. (come pow ())


@RTS - Uno sviluppatore di lingue è davvero alla ricerca di senso più che efficiente?

Un buon sviluppatore di un linguaggio di programmazione considera entrambi. Non posso dire nulla di Java. Ma in c ++ la funzione pow () viene calcolata al momento della compilazione. Ed è efficiente quanto i normali operatori.

@RTS: la pow()funzione esegue il suo calcolo in fase di esecuzione, a meno che tu non abbia un compilatore in grado di eseguire il fold costante pow(), di cui dubito fortemente. (Alcuni compilatori ti danno la possibilità di usare i intrinseci del processore per eseguire il calcolo.)
In silico

@In silico Non intendevo calcolare il valore finale, intendevo dire che i compilatori ottimizzeranno la chiamata di funzione, quindi hai solo l'equazione grezza.

2
@josefx: certo è una buona ragione. Un singolo *è un token lessicale, indipendentemente dal fatto che sia utilizzato per l'indirizzamento indiretto o la moltiplicazione. Un **elevamento a potenza significato sarebbe uno o due gettoni lessicali, e davvero non si desidera che il lexer di dover colpire la tabella dei simboli per tokenize.
David Thornley,

0

Il fatto è che gli operatori aritmetici sono solo scorciatoie di funzioni. (Quasi) Tutto ciò che fai con loro può essere fatto con una funzione. Esempio:

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

È solo più prolisso, quindi non vedo nulla di male nell'usare le funzioni per eseguire il "potere di".


-1

Addizione / sottrazione / negazione e moltiplicazione / divisione sono operatori matematici di base. Se dovessi rendere il potere un operatore, dove ti fermeresti? Operatore radice quadrata? Operatore N-root? Operatore logaritmico?

Non posso parlare per i loro creatori, ma posso dire che penso che sarebbe ingombrante e non ortogonale avere tali operatori nella lingua. Il numero di caratteri non alfa-numerici / spazi bianchi rimanenti sulla tastiera è piuttosto limitato. Così com'è, è strano che ci sia un operatore di modulo in C ++.


+1 - Non vedo perché avere modcome operatore sia strano. Di solito è una singola istruzione. È un'operazione primitiva su numeri interi. È usato soprattutto ovunque nell'informatica. (Implementare cose come buffer limitati senza modpuzzare)
Billy ONeal

@Billy ONeal: strano a causa dell'incongruenza tra la possibilità di utilizzare con tipi interi e tipi in virgola mobile. Assolutamente utile però e non mi sognerei di rimuoverlo. Solo eccentrico è tutto.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.