Gli operatori sono più chiari da leggere rispetto a parole chiave o funzioni? [chiuso]

13

È un po 'soggettivo, ma spero di ottenere una comprensione più chiara di quali fattori rendono chiaro l'utilizzo di un operatore rispetto a ottuso e difficile. Recentemente ho preso in considerazione la progettazione di una lingua e un problema su cui cerco sempre di eseguire operazioni chiave nella lingua di un operatore e quando utilizzare una parola chiave o una funzione.

Haskell è un po 'famoso per questo, poiché gli operatori personalizzati sono facili da creare e spesso un nuovo tipo di dati verrà fornito con diversi operatori da utilizzare su di esso. La libreria Parsec, ad esempio, ha un gran numero di operatori per combinare insieme i parser, con gemme come >. e .> Non riesco nemmeno a ricordare cosa significano in questo momento, ma ricordo che sono molto facili da lavorare con una volta avevo imparato a memoria cosa significano in realtà. Una chiamata di funzione come leftCompose(parser1, parser2) è stata migliore? Sicuramente più prolisso, ma in qualche modo più chiaro.

I sovraccarichi dell'operatore nei linguaggi C-like sono un problema simile, ma uniti dal problema aggiuntivo di sovraccaricare il significato di operatori familiari come + con nuovi significati insoliti.

In qualsiasi nuova lingua, questo potrebbe sembrare un problema piuttosto difficile. In F #, ad esempio, la trasmissione utilizza un operatore di tipo-casting derivato matematicamente, anziché la sintassi cast in stile C # o lo stile verbale VB. C #: (int32) x VB: CType(x, int32) F #: x :> int32

In teoria una nuova lingua potrebbe avere operatori per la maggior parte delle funzionalità incorporate. Invece di def o dec o var per la dichiarazione di variabili, perché non ! name o @ name o qualcosa di simile. Sicuramente abbrevia la dichiarazione seguita dal bind: @x := 5 anziché declare x = 5 o let x = 5 La maggior parte del codice richiederà molte definizioni di variabili, quindi perché no?

Quando un operatore è chiaro e utile e quando oscura?

    
posta CodexArcanum 15.03.2012 - 16:32
fonte

7 risposte

16

da un punto di vista del design generale del linguaggio non c'è bisogno di alcuna differenza tra funzioni e operatori. Si potrebbero descrivere funzioni come operazioni prefissate con qualsiasi (o persino variabile) arità. E le parole chiave possono essere viste come semplici funzioni o operatori con nomi riservati (utile nella progettazione di una grammatica).

Tutte queste decisioni alla fine si riducono a come vuoi che la notazione sia letta e che come dici tu sia soggettiva, sebbene si possano fare alcune ovvie razionalizzazioni, ad es. usa i soliti operatori infissi per la matematica come tutti li conoscono

Infine Dijkstra ha scritto una interessante giustificazione delle notazioni matematiche che ha usato, che include una buona discussione dei compromessi delle notazioni infisso vs prefisso

    
risposta data 15.03.2012 - 17:04
fonte
8

Per me, un operatore smette di essere utile quando non riesci più a leggere la riga di codice ad alta voce o nella tua testa e ha senso.

Ad esempio, declare x = 5 legge come "declare x equals 5" o let x = 5 può essere letto come "let x equal 5" , che è molto comprensibile se letto ad alta voce, tuttavia leggendo @x := 5 viene letto come "at x colon equals 5" (o "at x is defined to be 5" se sei un matematico), il che non ha alcun senso.

Quindi, a mio parere, usa un operatore se il codice può essere letto ad alta voce e compreso da una persona ragionevolmente intelligente che non ha familiarità con la lingua, ma usa i nomi delle funzioni se non.

    
risposta data 15.03.2012 - 16:46
fonte
4

Un operatore è chiaro e utile quando è familiare. E questo probabilmente significa che gli operatori di overload dovrebbero essere eseguiti solo quando l'operatore scelto è abbastanza vicino (come nella forma, nella precedenza e nell'associatività) come una pratica già consolidata.

Ma scavando un po 'di più, ci sono due aspetti.

La sintassi (infisso per operatore al posto del prefisso per la sintassi della chiamata della funzione) e la denominazione.

Per la sintassi, c'è un altro caso in cui infisso è abbastanza più chiaro del prefisso che potrebbe giustificare che lo sforzo diventi familiare: quando le chiamate sono incatenate e annidate.

Confronta a * b + c * d + e * f con +(+(*(a, b), *(c, d)), *(e, f)) o + + * a b * c d * e f (entrambi sono analizzabili nella stessa condizione della versione infissa). Il fatto che + separi i termini invece di precedere uno di essi da molto tempo lo rende più leggibile ai miei occhi (ma devi ricordare le regole di precedenza che non sono necessarie per la sintassi del prefisso). Se è necessario combinare le cose in questo modo, il modo di beneficiare a lungo termine valga il costo di apprendimento. E tu tieni questo vantaggio anche se dai nomi ai tuoi operatori con le lettere anziché usare i simboli.

Riguardo alla denominazione degli operatori, vedo alcuni vantaggi nell'usare qualcos'altro rispetto ai simboli stabiliti o al nome chiaro. Sì, potrebbero essere più brevi, ma sono davvero criptici e verranno presto dimenticati se non hai una buona ragione per conoscerli già.

Un terzo aspetto se si prende un POV di progettazione linguistica, è se l'insieme di operatori deve essere aperto o chiuso - si può aggiungere operatore o meno - e se la priorità e l'associatività devono essere specificabili dall'utente o meno. Il mio primo tentativo sarebbe di fare attenzione e non fornire priorità e associatività specificabili dall'utente, mentre sarei più aperto ad avere un set aperto (aumenterebbe il push per usare l'overloading dell'operatore, ma diminuirebbe quello per usare uno cattivo) solo per trarre vantaggio dalla sintassi infix dove è utile).

    
risposta data 15.03.2012 - 17:08
fonte
1

Gli operatori-come-simboli sono utili quando hanno un senso intuitivo. + e - sono ovviamente addizioni e sottrazioni, per esempio, che è il motivo per cui praticamente ogni lingua li usa come loro operatori. Lo stesso con il confronto ( < e > vengono insegnate nella scuola elementare, e <= e >= sono estensioni intuitive dei confronti di base quando capisci che non ci sono tasti di confronto sottolineati sulla tastiera standard.)

* e / sono meno immediatamente evidenti, ma sono utilizzati universalmente e la loro posizione sul tastierino numerico proprio accanto alle chiavi + e - aiuta a fornire il contesto. C's << e >> si trovano nella stessa categoria: quando capisci cosa sono uno spostamento a sinistra e uno a destra, non è troppo difficile capire i mnemonici dietro le frecce.

Poi vieni ad alcuni di quelli veramente strani, roba che può trasformare il codice C in zuppa di operatore illeggibile. (Prendendo in considerazione C qui perché è un esempio molto pesante per l'operatore, la cui sintassi è praticamente comune a tutti, sia lavorando con C che con i linguaggi discendenti.) Ad esempio, quindi % operator. Tutti sanno cosa sia: è un segno di percentuale ... giusto? Tranne che in C, non ha nulla a che fare con la divisione per 100; è l'operatore del modulo. Questo ha senso mnemonico, ma è così!

Peggio ancora sono gli operatori booleani. & come e ha senso, tranne che ci sono due operatori & diversi che fanno due cose diverse, ed entrambi sono sintatticamente validi in ogni caso in cui uno di essi è valido, anche se solo uno di essi ha effettivamente senso in ogni caso specifico. Questa è solo una ricetta per la confusione! Gli operatori o , xor e not sono anche peggio, poiché non usano simboli mnemonicamente utili e non sono nemmeno coerenti. (Nessun operatore double- xor e logico e bit per bit non utilizzano due simboli diversi anziché un simbolo e una versione raddoppiata.)

E proprio per peggiorare, gli operatori & e * vengono riutilizzati per cose completamente diverse, il che rende più difficile l'analisi della lingua sia per le persone che per i compilatori. (Cosa significa A * B ? Dipende interamente dal contesto: è A un tipo o una variabile?)

Certo, non ho mai parlato con Dennis Ritchie e gli ho chiesto delle decisioni progettuali che ha preso nella lingua, ma così tanto sembra che la filosofia dietro gli operatori fosse "simboli per il gusto dei simboli".

Confrontalo con Pascal, che ha gli stessi operatori di C, ma una diversa filosofia nel rappresentarli: gli operatori dovrebbero essere intuitivi e facili da leggere. Gli operatori aritmetici sono gli stessi. L'operatore modulo è la parola mod , perché sulla tastiera non c'è alcun simbolo che ovviamente significa "modulo". Gli operatori logici sono and , or , xor e not , e ce n'è solo uno di ciascuno; il compilatore sa se hai bisogno della versione booleana o bit a bit a seconda che tu stia operando su booleani o numeri, eliminando un'intera classe di errori. Questo rende lontano il codice Pascal più facile da capire rispetto al codice C, specialmente in un editor moderno con evidenziazione della sintassi in modo che gli operatori e le parole chiave siano visivamente distinti dagli identificatori.

    
risposta data 15.03.2012 - 18:00
fonte
1

Penso che gli operatori siano molto più leggibili quando la loro funzione rispecchia da vicino il loro scopo familiare e matematico. Ad esempio, gli operatori standard come +, -, ecc. Sono più leggibili di Add o Sottrai quando si eseguono addizioni e sottrazioni. Il comportamento dell'operatore deve essere chiaramente definito. Posso tollerare un sovraccarico del significato di + per la concatenazione di liste, per esempio. Idealmente l'operazione non avrebbe effetti collaterali, restituendo un valore invece di mutare.

Ho faticato con gli operatori di collegamento per funzioni come fold, però. Ovviamente più esperienza con loro faciliterebbe questo, ma trovo che foldRight più leggibile di /: .

    
risposta data 15.03.2012 - 17:39
fonte
1

Il problema con gli operatori è che c'è solo un piccolo numero di essi rispetto al numero di nomi di metodi sensical (!) che potrebbero essere usati al loro posto. Un effetto collaterale è che gli operatori definibili dall'utente tendono a introdurre un sacco di sovraccarico.

Certamente, la riga C = A * x + b * c è più facile da scrivere e leggere di C = A.multiplyVector(x).addVector(b.multiplyScalar(c)) .

O, beh, è certamente più facile da scrivere, se hai tutte le versioni sovraccaricate di tutti quegli operatori nella tua testa. E puoi leggerlo in seguito, mentre correggi il penultimo errore.

Ora, per la maggior parte del codice che sta per scadere, va bene. "Il progetto passa tutti i test, e funziona" - per molti un software che è tutto ciò che potremmo desiderare.

Ma le cose funzionano diversamente quando stai facendo software che entra in aree critiche. Roba di sicurezza. Sicurezza. Software che mantiene gli aerei in aria o che mantiene in funzione gli impianti nucleari. O software che crittografa le tue e-mail altamente confidenziali.

Per gli esperti di sicurezza specializzati nel codice di controllo, questo può essere un incubo. Il mix di una grande varietà di operatori definiti dall'utente e il sovraccarico dell'operatore può lasciarli nella spiacevole situazione in cui hanno difficoltà a capire quale codice verrà eseguito alla fine.

Quindi, come affermato nella domanda originale, questo è un argomento molto soggettivo. E mentre molti suggerimenti su come dovrebbero essere usati gli operatori potrebbero sembrare perfettamente sensoriali, la combinazione di pochi di loro potrebbe creare molti problemi a lungo termine.

    
risposta data 15.03.2012 - 22:41
fonte
0

Suppongo che l'esempio più estremo sia APL il seguente programma è il "gioco della vita":

life←{↑1 ⍵∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵}

E se riesci a capire cosa significa tutto senza passare un paio di giorni con il manuale di riferimento, allora buona fortuna a te!

Il problema è che hai una serie di simboli che quasi tutti comprendono intuitivamente: +,-,*,/,%,=,==,&

e il resto che richiede qualche spiegazione prima che possano essere capiti e, generalmente, sono specifici per la lingua specifica. Ad esempio non esiste un simbolo ovvio per specificare quale membro di un array si desidera, [], () e persino "." sono stati usati, ma allo stesso modo non esiste una parola chiave ovvia che potresti facilmente utilizzare, quindi c'è un caso da fare per un uso giudizioso degli operatori. Ma non troppi di loro.

    
risposta data 16.03.2012 - 10:57
fonte

Leggi altre domande sui tag