[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [volume] [parte]


Capitolo 564.   BC

BC è un interprete di un linguaggio aritmetico, che fa parte della tradizione dei sistemi Unix, tanto da essere codificato anche nello standard POSIX. Come linguaggio non ha nulla di speciale, ma la sua facilità di utilizzo in modo interattivo e la sua diffusione, lo rendono molto comodo e utile.

L'utilizzo più conveniente di BC è probabilmente quello a riga di comando, come calcolatrice, tenendo conto che questa sua caratteristica può anche essere sfruttata utilmente all'interno di script di shell. L'esempio seguente mostra un utilizzo interattivo, per comprendere di cosa si tratta, almeno a prima vista:

bc[Invio]

255*63*3737*512[Invio]

30737871360

[Ctrl d]

Quello che si vede nell'esempio è la moltiplicazione di tre numeri: 255, 63, 3 737 e 512. Il risultato è ciò che si vede alla fine: 30 737 871 360. La stessa cosa si potrebbe inserire in uno script di shell nel modo seguente, in cui il risultato della moltiplicazione viene assegnato alla variabile RISULTATO:

...
RISULTATO=`echo "255*63*3737*512" | bc`
...

Tuttavia, BC è in realtà un linguaggio di programmazione, benché semplice, la cui caratteristica fondamentale è quella di poter definire l'approssimazione del risultato, indipendentemente dall'architettura dell'elaboratore per il quale è stato compilato.

564.1   Base di numerazione

Una caratteristica importante di BC è la possibilità di gestire basi di numerazione diverse da 10. Tuttavia, ciò può creare degli imprevisti inattesi, per cui occorre fare attenzione quando si tenta di modificare la convenzione normale.

La base di numerazione viene modificata intervenendo attraverso due variabili predefinite che fanno parte del linguaggio, denominate ibase e obase. La prima contiene la base di numerazione per i numeri che vengono inseriti, mentre la seconda contiene la base usata per la rappresentazione dei risultati. In condizioni normali, sia ibase, sia obase, contengono il valore 10. Tuttavia, quando si cambia il valore di ibase si possono creare delle complicazioni; supponendo di voler inserire valori in base otto, basta agire come segue:

ibase=8[Invio]

Nel momento in cui si scrive un valore, questo viene interpretato in base otto:

777[Invio]

511

Infatti, 7778 equivale a 511.

Quando però si vuole intervenire nuovamente sulla variabile ibase, occorre ricordare che per il momento la base di numerazione è otto. Pertanto, volendo tornare alla base 10, bisogna trasformare prima il valore in ottale: 128.

ibase=12[Invio]

777[Invio]

777

Diversamente, scrivendo nuovamente ibase=10 non si cambierebbe la base di numerazione, perché quel numero andrebbe inteso in ottale.

Esiste anche una convenzione, per cui i valori numerici espressi con una sola cifra, vanno intesi correttamente, in modo indipendente dal valore della variabile ibase. Pertanto, 9 vale sempre come se fosse scritto in base 10, dal momento che non ci possono essere ambiguità anche se la base di numerazione fosse più grande.

Le cifre che possono essere usate per comporre un numero sono i simboli da 0 a 9, con le lettere maiuscole da A a F. In questo modo si possono rappresentare agevolmente numeri con basi di numerazione che vadano da 2 a 16, mentre per basi di numerazione superiori le cose si complicano. In pratica, si possono rappresentare basi superiori scrivendo il risultato a cifre separate, dove ogni cifra è espressa come un numero in base 10. Per esempio, la stringa «100», esprimente un numero in base 20, verrebbe rappresentato come 01 00 00.(1) L'esempio seguente mostra in che modo arrivare a questo risultato, tenendo in considerazione il fatto che la variabile ibase contenga inizialmente il valore 10.

obase=20[Invio]

400[Invio]

01 00 00

In base al principio per il quale una cifra numerica singola viene interpretata in modo non ambiguo, indipendentemente dalla base di numerazione stabilita in ingresso con la variabile ibase, si può tornare facilmente a un inserimento di valori in base 10, sfruttando la cifra A, il cui valore è sempre pari a 10:

ibase=A[Invio]

564.2   Approssimazione

La variabile scale definisce la quantità di cifre decimali da prendere in considerazione, quando il contesto non esprime già questo valore. In altri termini, una moltiplicazione definisce già la quantità di cifre decimali da considerare:

10*2.45[Invio]

24.50

Al contrario, nel caso della divisione è necessario stabilire subito la quantità di decimali da considerare:

scale=4[Invio]

10/3[Invio]

3.3333

Generalmente, all'avvio dell'interprete, il valore della variabile scale è pari a zero, avendo così un'approssimazione predefinita alla parte intera.

564.3   Linguaggio di programmazione

Il linguaggio di BC ha una vaga somiglianza con il C. In generale, le righe vuote e quelle bianche vengono ignorate, così come il testo circoscritto tra /* e */ (proprio come nel C).

Alcune realizzazioni di BC prevedono anche l'uso del simbolo # come commento, allo scopo di poter realizzare facilmente degli script, iniziando con #!/usr/bin/bc, ma si tratta di un'estensione che non fa parte dello standard POSIX.

Le istruzioni del linguaggio BC terminano normalmente alla fine della riga, ma è possibile usare anche il punto e virgola (;) se si preferisce, oppure se si vogliono indicare più istruzioni assieme sulla stessa riga. La continuazione di un'istruzione in una riga successiva si ottiene mettendo una barra obliqua inversa (\) alla fine, esattamente prima del codice di interruzione di riga.

istruzione[;]
istruzione\
continuazione_istruzione

Si possono definire dei raggruppamenti di istruzioni, racchiudendoli tra parentesi graffe ({ }). Anche in questo caso le istruzioni possono essere separate attraverso interruzioni di riga, oppure con il punto e virgola.

{istruzione
istruzione
istruzione}
{istruzione; istruzione; istruzione}

Il linguaggio consente la dichiarazione di variabili e di funzioni, che possono avere un nome composto esclusivamente da una lettera minuscola. Alcune realizzazioni di BC consentono l'uso di nomi più articolati, ma si tratta di estensioni non compatibili con le specifiche POSIX.

Il linguaggio BC non prevede una funzione principale, come avviene invece in C. Infatti, si tratta di un linguaggio interpretato dove tutto viene eseguito appena possibile; anche le funzioni esistono appena dichiarate e possono essere sostituite da una dichiarazione successiva che utilizza lo stesso nome

Esistono solo due tipi di dati: le stringhe delimitate e i valori numerici (con la quantità stabilita di cifre dopo la virgola), dove la separazione tra parte intera e parte decimale si indica esclusivamente con un punto (.). Tuttavia, alle variabili si possono assegnare solo numeri, così come le funzioni possono restituire solo valori numerici.

564.3.1   Variabili semplici e array

La dichiarazione di una variabile avviene in modo molto semplice, con l'assegnamento di un valore numerico, come nell'esempio seguente:

x=123.456

Nello stesso modo si possono dichiarare degli array a una sola dimensione, indicando un indice tra parentesi quadre, come nell'esempio seguente, dove in particolare l'indice è espresso da un'espressione:

x[1+2]=234.567

Gli array non devono essere dimensionati e possono usare la quantità massima di elementi disponibili in base alla realizzazione di BC. Il primo elemento si raggiunge con l'indice zero e gli elementi successivi sono numeri interi positivi. Se si fa riferimento a un elemento dell'array che non è ancora stato assegnato, si ottiene il valore zero.

Per fare riferimento a un array nel suo complesso, si indica il nome, seguito dalle parentesi quadre, aperte e chiuse, senza contenere alcun indice: x[].

564.3.2   Funzioni

La dichiarazione di una funzione ha una forma precisa, dove in questo caso x rappresenta il nome della stessa:

define x([parametro[, parametro]]...) {
    [auto variabile_automatica[, variabile_automatica]...]
    [istruzione]
    ...
    ...
    [return [(varlore_restituito)]]
}

Si osservi in particolare l'uso delle parentesi graffe per delimitare il corpo della funzione: è indispensabile che la parentesi graffa aperta si trovi sulla stessa riga iniziale della dichiarazione della funzione, con i parametri relativi.

I parametri della funzione possono essere nomi di variabili normali, oppure nomi di array senza un indice tra le parentesi quadre.

I parametri che appaiono tra parentesi tonde, equivalgono alla dichiarazione implicita di variabili locali, definite di tipo automatico, contenenti il valore trasmesso al momento della chiamata.

Oltre alle variabili che compongono l'elenco dei parametri della funzione, si possono dichiarare altre variabili automatiche nel modo seguente, nella riga immediatamente successiva alla parentesi graffa aperta:

[auto variabile_automatica[, variabile_automatica]...]

Si può usare una sola istruzione auto, nella quale vanno elencate tutte le variabili automatiche, compresi gli array, nella forma x[].

Una funzione restituisce sempre un valore numerico, anche se non viene utilizzata esplicitamente l'istruzione return; in tal caso, si tratta sempre di zero.

Se il valore restituito dalla funzione non viene usato nella chiamata per un assegnamento, questo viene visualizzato, anche se ciò non fosse desiderabile. Per evitare questo inconveniente, è possibile assegnare a una variabile fittizia il valore restituito dalla funzione.

Anche se è possibile fornire un array come parametro in una chiamata di funzione, l'istruzione return non può restituire un array.

La chiamata di una funzione avviene nel modo seguente; anche in questo caso x rappresenta il nome della funzione chiamata:

x([parametro[, parametro]]...)

I parametri possono essere variabili oppure valori costanti. Nel primo caso, se la funziona cambia il contenuto delle variabili corrispondenti, tali modifiche non si ripercuotono nelle variabili usate nella chiamata.

Le funzioni possono anche non avere parametri; in quei casi si indicano le parentesi tonde senza alcun contenuto, sia nella dichiarazione, sia nella chiamata.

L'esempio seguente, molto semplice, mostra la dichiarazione di una funzione che esegue la moltiplicazione:

define m (x, y) {
    auto z
    z=x*y
    return (z)
}

Se questa funzione venisse salvata nel file moltiplica, si potrebbe usare BC nel modo seguente:

bc moltiplica[Invio]

m (7, 2)[Invio]

14

[Ctrl d]

La parola chiave return, può essere usata senza l'indicazione del valore da restituire e quindi senza le parentesi tonde. In tal caso viene restituito il valore zero.

564.3.3   Emissione delle informazioni

BC prevede poche funzioni predefinite (interne), ma non mette a disposizione una funzione per l'emissione di stringhe. Se necessario, una costante stringa viene visualizzata semplicemente indicandola come un'istruzione, con un piccolo accorgimento.

Un'espressione che si traduce in un numero, porta alla visualizzazione del risultato, seguito da un codice di interruzione di riga; pertanto, l'esempio seguente genera il risultato 15 783 552: il valore viene visualizzato e il cursore si trova poi collocato sulla riga successiva:

4567*3456[Invio]

15783552
_

Al contrario, la visualizzazione di una stringa non fa avanzare alla riga successiva, permettendo l'aggiunta di altre stringhe e di un solo valore numerico finale.

"ciao " ; "amore " ; "bello!"[Invio]

Infatti, l'esempio si traduce nel testo seguente, con il cursore alla destra del punto esclamativo:

ciao amore bello!_

Aggiungendo un numero, la visualizzazione sulla riga termina:

"Anni: " ; 35[Invio]

Anni: 35
_

Il risultato delle espressioni viene visualizzato se questo non viene catturato da un assegnamento a una variabile. Pertanto, l'esempio seguente visualizza il risultato:

7*5[Invio]

35

Invece, l'esempio successivo non visualizza alcunché:

a=7*5[Invio]

Tuttavia, è possibile mostrare il risultato di un'espressione il cui risultato viene assegnato a una variabile, racchiudendola all'interno di parentesi tonde, come nell'esempio seguente:

(a=7*5)[Invio]

35

564.3.4   Espressioni

Gli operatori che intervengono su valori numerici sono elencati nella tabella 564.17. Esiste tuttavia un chiarimento da fare sull'espressione op1%op2, che non si comporta secondo lo standard comune. Infatti, solo quando scale contiene il valore zero, il risultato è il resto della divisione intera; diversamente, si ottiene il resto della divisione, tolto il risultato ottenuto in base alla quantità di cifre decimali stabilito dalla variabile scale. Per esempio, se scale contiene il valore cinque, 10%3 genera il risultato 0,000 01. Infatti, potendo gestire cinque cifre decimali, 10%3 dà il risultato 3,333 33, per cui, il resto della divisione rimane solo 0,000 01:
3,333 33*3+0,000 01=10.

Tabella 564.17. Elenco degli operatori aritmetici e di quelli di assegnamento relativi a valori numerici.

Operatore e
operandi
Descrizione
++op
Incrementa di un'unità l'operando prima che venga restituito il suo valore.
op++
Incrementa di un'unità l'operando dopo averne restituito il suo valore.
--op
Decrementa di un'unità l'operando prima che venga restituito il suo valore.
op--
Decrementa di un'unità l'operando dopo averne restituito il suo valore.
+op
Non ha alcun effetto.
-op
Inverte il segno dell'operando.
op1 + op2
Somma i due operandi.
op1 - op2
Sottrae dal primo il secondo operando.
op1 * op2
Moltiplica i due operandi.
op1 / op2
Divide il primo operando per il secondo.
op1 % op2
Modulo: il resto della divisione tra il primo e il secondo operando.
op1 ^ op2
Esponente: il primo operando elevato alla potenza del secondo.
x = valore
Assegna alla variabile il valore alla destra.
( espressione )
Le parentesi tonde richiedono la precedenza nella valutazione dell'espressione.
op1 += op2
op1 = op1 + op2
op1 -= op2
op1 = op1 - op2
op1 *= op2
op1 = op1 * op2
op1 /= op2
op1 = op1 / op2
op1 %= op2
op1 = op1 % op2

Alcune realizzazioni tradizionali di BC, non più standard secondo POSIX, consentono l'uso di operatori simili al tipo op=, descritti nella tabella, ma invertiti nell'ordine: =op. Ciò crea un problema nella valutazione di alcuni tipi di espressione; per esempio, a=-1 può significare l'assegnamento del valore -1 alla variabile a, oppure l'assegnamento di a-1. Per evitare ambiguità in queste condizioni, conviene usare le parentesi: a=(-1).

Tabella 564.18. Elenco degli operatori di assegnamento obsoleti che qualche realizzazione di BC potrebbe usare ancora.

Operatore e
operandi
Equivalenza
op1 =+ op2
op1 = op1 + op2
op1 =- op2
op1 = op1 - op2
op1 =* op2
op1 = op1 * op2
op1 =/ op2
op1 = op1 / op2
op1 =% op2
op1 = op1 % op2

Gli operatori di confronto determinano la relazione tra due operandi e possono essere utilizzati esclusivamente in alcuni contesti precisi. Vengono elencati gli operatori disponibili nella tabella 564.19.

Tabella 564.19. Elenco degli operatori di confronto. Le metavariabili indicate rappresentano gli operandi e la loro posizione.

Operatore e
operandi
Descrizione
op1 == op2
Vero se gli operandi si equivalgono.
op1 != op2
Vero se gli operandi sono differenti.
op1 < op2
Vero se il primo operando è minore del secondo.
op1 > op2
Vero se il primo operando è maggiore del secondo.
op1 <= op2
Vero se il primo operando è minore o uguale al secondo.
op1 >= op2
Vero se il primo operando è maggiore o uguale al secondo.

Lo standard POSIX stabilisce che queste espressioni possono apparire solo come condizione delle istruzioni if, while e for; inoltre, è esclusa la possibilità di comporre espressioni più complesse con l'uso di operatori booleani.

564.3.5   Funzioni standard

BC predispone poche funzioni standard, che si distinguono in particolare per la lunghezza del loro nome. Queste sono riepilogate in breve nella tabella 564.20.

Tabella 564.20. Funzioni interne.

Funzione Valore restituito
length ( espressione )
Quantità di cifre significative dell'espressione.
scale ( espressione )
Quantità di cifre decimali dell'espressione.
sqrt ( espressione )
Radice quadrata dell'espressione.

Per quanto riguarda il caso particolare di scale(), si fa riferimento al numero di decimali che genera l'espressione, in base al contesto. Per esempio, se il valore della variabile scale è zero, qualunque divisione dà soltanto un risultato intero, per cui scale() restituisce sempre solo zero.

Oltre a queste funzioni, è possibile chiedere a BC di mettere a disposizione alcune funzioni da una libreria standard. Si tratta di quelle elencate nella tabella 564.21.

Tabella 564.21. Funzioni della libreria standard.

Funzione Valore restituito
s ( x )
Seno.
c ( x )
Coseno.
a ( x )
Arcotangente.
l ( x )
Logaritmo naturale.
e ( x )
Funzione esponenziale: e elevato alla x.
j ( n, x )
Funzione di Bessel.

564.3.6   Strutture di controllo di flusso

Il linguaggio BC gestisce le strutture di controllo di flusso principali, anche se con qualche limitazione. È disponibile una struttura condizionale semplificata (senza l'analisi di un'alternativa), il ciclo iterativo e il ciclo enumerativo:

if (condizione) istruzione
while (condizione) istruzione
for (espressione1; espressione2; espressione3) istruzione

Come nel linguaggio C, dal momento che si possono raggruppare le istruzioni in blocchi racchiusi tra parentesi graffe, in pratica si utilizzano queste strutture nel modo seguente:

if (condizione) {
    istruzione
    ...
}
while (condizione) {
    istruzione
    ...
}
for (espressione1; espressione2; espressione3) {
    istruzione
    ...
}

Naturalmente, le tre espressioni tra parentesi del ciclo enumerativo vanno intese nel modo comune. Per esempio, ciò che appare di seguito serve a mostrare 10 «x», attraverso il conteggio di una variabile.

for (i = 0; i < 10; i++) {
    "x"
}

Nell'ambito dei cicli, è possibile usare l'istruzione break per interrompere il ciclo con un'uscita forzata.

564.4   Utilizzo di BC

L'interprete del linguaggio BC è l'eseguibile bc, che si utilizza secondo la sintassi seguente:

bc [-l] [file_bc]...

L'interprete legge ed esegue tutti i file indicati come argomento della riga di comando; alla fine, legge lo standard input. L'interprete termina di funzionare quando il flusso dello standard input termina, oppure quando incontra l'istruzione quit.

In questo modo, un programma che si deve concludere deve contenere l'istruzione quit, oppure deve essere fornito attraverso lo standard input.

L'opzione -l serve a ottenere da BC la disponibilità delle funzioni di libreria standard, elencate nella tabella 564.21; inoltre, la variabile scale viene impostata al valore 20, mentre in condizioni normali il suo valore predefinito è zero.

Lo standard POSIX non prevede l'uso del simbolo # come commento, per cui non è possibile realizzare degli script se non sfruttando delle estensioni di realizzazioni speciali. In pratica, ci possono essere realizzazioni di BC che consentono di scrivere programmi che iniziano in modo simile a quello seguente, eventualmente con l'aggiunta dell'opzione -l, a cui poi si aggiungono i permessi di esecuzione, ma ciò non è possibile se si vogliono scrivere programmi standard (portabili).

#!/usr/bin/bc

564.5   BC nella realizzazione GNU

La realizzazione GNU di BC (2) consente l'uso di diverse estensioni rispetto allo standard POSIX; in particolare completa la struttura di controllo condizionale con l'alternativa else, aggiunge l'istruzione print per una gestione migliore della visualizzazione di informazioni e consente l'uso di operatori booleani nelle espressioni logiche, che possono essere usate anche al di fuori del contesto restrittivo stabilito da POSIX. Tuttavia è possibile richiedere un funzionamento strettamente aderente allo standard POSIX, utilizzando l'opzione -s, oppure creando la variabile di ambiente POSIXLY_CORRECT.

L'eseguibile bc consente l'uso di più opzioni della riga di comando, alcune delle quali vengono descritte brevemente nel seguito.

Opzione Descrizione
-l
--mathlib
Richiede l'uso delle librerie matematiche standard, impostando la variabile scale al valore 20.
-w
--warn
Segnala l'uso di estensioni allo standard POSIX.
-s
--standard
Restringe il funzionamento allo standard POSIX.

564.6   Riferimenti

Appunti di informatica libera 2007.02 --- Copyright © 2000-2007 Daniele Giacomini -- <daniele (ad) swlibero·org>


1) uno, zero, zero in base 20, corrisponde a 400.

2) GNU BC   GNU GPL


Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome bc.htm

[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico]

Valid ISO-HTML!

CSS validator!