Aritmetica 2


Quando il codice Javascript diventa corposo, conviene evitare di appesantire i sorgenti HTML, separando la redazione delle pagine da quella del codice, scrivendolo e salvandolo in files appositi con estensione *.js da richiamare nelle pagine HTML con un tag script nel seguente modo:

<script src="nomefile.js"></script>

Come esempio di questa tecnica si propone la seguente pagina, che è una versione ampliata dell'esempio 4 in cui non ci si limita a controllare se un numero è primo o ammette almeno un divisore, ma si deduce la sua completa scomposizione in potenze di fattori primi.

Questo è il sorgente

<!DOCTYPE html>
<html lang="it">
<head> 
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>Esempio 5
  <script src="scomposizione.js"></script>
</head>

<body>
<form id="frm" name="frm" 
   style="position:relative; width:80%; left:10%; top:40px;
   background-color:azure; color:blue; 
   border-style:solid; border-color:blue; text-align:center">
<p>
<h2 style="text-align:center">scomposizione in fattori primi</h2>
<label for="campo_numero">numero naturale </label>
<input type="text" id="campo_numero" size="10" value="2" />
</p>
<p>
<input type="button" value=" scomposizione" 
  onclick="campo_scomposizione.value=scomposizione(campo_numero.value)" />
</p>
<p>
<input type="text" id="campo_scomposizione" name="campo_scomposizione"
   size="80%;" value="" readonly="readonly" />
</p>
<p><input type="reset" value=" reset" /></p>
</form>
</body>
</html>

Come si vede, il sorgente Javascript non è immesso nel sorgente HTML, ma, scritto nel file separato scomposizione.js, viene solo richiamato dall'interno di un tag script.

In questo caso, per presentare all'utente l'output della funzione non si usa un messaggio alert(), ma si predispone nella form della pagina un campo con attributo readonly, il cui valore, quando viene attivato il bottone scomposizione, viene aggiornato al valore prodotto dalla funzione.

La form, quindi, oltre al titolo, contiene:

HTML quindi comunica con il codice Javascript inviandogli l'argomento della funzione scomposizione() e raccogliendone il valore prodotto.

Il sorgente Javascript contenuto nel file scomposizione.js è il seguente.

//variabili globali
var risultato = "";
var base = 2;
var esponente = 0;

/* funzione ausiliaria per la formazione della stringa 
risultato; questa funzione può accedere ai valori 
delle variabili risultato, base, esponente 
proprio perché sono variabili globali.*/

function aggiornaRisultato()
{
  if (risultato.length>0) risultato += " · ";
  risultato += base;
  if (esponente>1)
    risultato += "^"+esponente;
  esponente = 0;
}

// funzione principale per l'elaborazione matematica

function scomposizione(numero)
  {
// controllo dell'input
    if (isNaN(numero))
/* l'argomento di return è il valore che 
la funzione ritrasmette ad HTML */
      return(numero+" non è un numero.");
// altri controlli dell'input
    if (numero!=parseInt(numero))
      return(numero+" non è un numero intero.");
    if (numero<2)
      return("Solo numeri maggiori di 1.");
/* superati i controlli, si può procedere;
si inizializzano le variabili;
nella variabile dividendo si copia il valore di numero:
mentre numero mantiene il valore dell'input, 
il valore di dividendo cambierà ad ogni divisione */
    risultato = "";
    var dividendo = numero;
    var n_fattori = 0;
    esponente=0;
    base = 2; 
/* se il numero è pari, dividerlo per 2
fino a che non si ottiene un resto dispari */
    while ((dividendo & 1)==0)// finché dividendo è pari
      {
        dividendo >>= 1; // dividerlo per 2
        esponente++;  // e incrementare di 1 l'esponente
        n_fattori++;  // e il numero di fattori
      }
/* se sono state eseguite divisioni per 2
registrare la potenza di 2 in risultato */
    if (esponente>0)
      aggiornaRisultato();

/* se è rimasto un dividendo maggiore di 1
provare se è divisibile per 3 e per i successivi dispari
fino alla radice quadrata di numero */

    var radiceq = Math.sqrt(numero);
    base = 3;
    esponente = 0;

    while ((dividendo>1) && (base <= radiceq))
      if ((dividendo % base)==0)  // se base è un divisore 
        {
          n_fattori++;
          dividendo /= base;
          esponente++;
        }
      else
        {
          if (esponente>0)  // se si sono trovati divisori
            {
              n_fattori++;
              aggiornaRisultato();
            }
          base += 2;
        }
    if (dividendo>1)   // se è rimasto un numero senza divisori
      {
        n_fattori++;
        base = dividendo;
        esponente = 1;
        aggiornaRisultato();
      }
    else
      if (esponente>0)   // se si sono trovati divisori
        aggiornaRisultato();
  
    if (n_fattori==1)   // se non si sono trovati divisori
      return(numero+" è primo.")
    else
      return(risultato)  // in  risultato si è formata la scomposizione     
  }

Il sorgente Javascript ha innanzitutto la dichiarazione di tre variabili fuori dal corpo di qualche funzione: le variabili così collocate sono variabili globali, cioè il loro valore è permanente ed accessibile da parte di tutte le funzioni del sorgente. Se fossero state dichiarate, ad esempio, all'interno del corpo della funzione scomposizione() sarebbero state variabili locali di questa funzione, accessibili solo all'interno della funzione e distrutte al termine della funzione stessa.

Segue poi la funzione aggiornaRisultato(): questa funzione ha un ruolo ausiliario alla funzione scomposizione(): è introdotta solo per evitare di ripetere più volte lo stesso gruppo di istruzioni all'interno della funzione scomposizione().

Viene infine la funzione scomposizione(): le istruzioni più significative sono commentate in rosso.

Questa funzione riceve il dato da analizzare dal campo campo_numero della form della pagina HTML. All'interno della funzione questo dato è rappresentato dalla variabile locale numero.

Si effettuano vari controlli sull'adeguatezza del dato (deve rappresentare un numero intero maggiore di 1). Nel caso che un controllo fallisca, l'elaborazione viene terminata da istruzioni return(): un'istruzione return() fornita di argomento non solo interrompe l'elaborazione ma restituisce al mittente il suo argomento. Nei casi dell'esempio return() spedisce ad HTML messaggi di errore che HTML visualizza nel campo campo_scomposizione.

Se il numero è adeguato, l'elaborazione può procedere.

Si assegna il valore numero alla variabile locale dividendo che assumerà i valori dei quozienti man mano che si individuano divisori.

Per prima cosa si controlla se dividendo è pari. Per sapere se un numero naturale è pari, basta controllare il suo bit di ordine più basso. A questo scopo si fa un'operazione and bitwise tra il numero e 1: se risulta zero, il numero è pari.

Se il numero è pari, lo si divide per 2; se si ottiene un quoziente pari si divide anche questo per 2 e si continua così fino a che non si ottiene un risultato dispari. Per ogni divisione effettuata si aumenta di un'unità il valore della variabile esponente.

Per dividere un numero intero pari per 2 è utile lo shift destro. Viceversa lo shift sinistro moltiplica un numero intero per 2.

Al termine di questo processo (se è stato possibile) si registra nella variabile risultato una potenza di base 2 ed esponente uguale al numero di divisioni fatte.

Se l'ultimo quoziente ottenuto (il cui valore è sempre in dividendo ed è dispari) è 1 il processo è finito, altrimenti bisogna controllare se il numero ammette divisori dispari a partire da 3 fino ad un massimo che non può superare la radice del numero stesso.

Ogni volta che si trova un divisore si divide dividendo e si aumenta il valore di esponente fintanto che è possibile avere un quoziente esatto. Quando non è più possibile, si registra la nuova potenza e si passa al dispari successivo. Il processo termina quando il dispari successivo supera la radice quadrata del numero iniziale.

Se dopo questo superamento dividendo non rimane 1, significa che è un numero primo che va aggiunto alla stringa scomposizione.

Se alla fine non si è trovato neanche un divisore, n_fattori è rimasto 1 e ciò significa che il numero è un numero primo, altrimenti si pone nell'argomento del return() finale la stringa scomposizione formatasi durante l'elaborazione.

La visibilità (scope) di una variabile è limitata al blocco in cui è dichiarata.

Un blocco è un insieme di istruzioni successive delimitato da una coppia di parentesi graffa.

Quindi se una variabile è dichiarata nel corpo di una funzione il suo valore non è accessibile dall'esterno della funzione stessa. Una variabile di questo tipo è detta locale: viene creata al momento della sua dichiarazione e cancellata all'uscita dal blocco.

Se si vuole una variabile permanente ed accessibile da tutte le funzioni essa deve essere dichiara esternamente alle funzioni. Variabili di questo tipo sono dette globali.

Un'istruzione return nel corpo di una funzione termina l'elaborazione saltando tutte le istruzioni successive.

Se return è seguita da un valore, la funzione rende disponibile all'esterno il valore stesso.

Per sapere se un numero naturale è pari, basta controllare il suo bit di ordine più basso. A questo scopo si fa un'operazione and bitwise tra il numero e 1: se risulta zero, il numero è pari.

Per dividere un numero intero pari per 2 è utile lo shift destro. Viceversa lo shift sinistro moltiplica un numero intero per 2.