Familiarizzare con Mathematica
Transcript
Familiarizzare con Mathematica
Il contenuto di queste pagine è protetto dalle leggi sul copyright e dalle disposizione dei trattati internazionali. Il titolo ed il copyright relativi alle pagine sono di proprietà dell'autore. Le pagine possono essere riprodotte ed utilizzate liberamente dagli studenti, dagli istituti di ricerca, scolastici ed universitari afferenti al Ministero dell'Istruzione, dell'Università e della Ricerca per scopi istituzionali, non a fine di lucro. Ogni altro utilizzo o riproduzione (ivi incluse, ma non limitatamente a, le riproduzioni a mezzo stampa, su supporti magnetici o su reti di calcolatori) in toto o in parte è vietata, se non esplicitamente autorizzata per iscritto, a priori, da parte dell'autore. L'informazione contenuta in queste pagine è ritenuta essere accurata alla data della pubblicazione. Essa è fornita per scopi meramente didattici e non per essere utilizzata in progetti di impianti, prodotti, ecc. L'informazione contenuta in queste pagine è soggetta a cambiamenti senza preavviso. L'autore non si assume alcuna responsabilità per il contenuto di queste pagine (ivi incluse, ma non limitatamente a, la correttezza, completezza, applicabilità ed aggiornamento dell'informazione). In ogni caso non può essere dichiarata conformità all'informazione contenuta in queste pagine. In ogni caso questa nota di copyright non deve mai essere rimossa e deve essere riportata anche in utilizzi parziali. Familiarizzare con Mathematica 1.1. Input e output di Mathematica Una volta eseguito Mathematica, l'utente si trova di fronte a una finestra inizialmente vuota. Questa finestra è associata ad un notebook, che è la componente di Mathematica deputata a interagire con l'utente. Come indicato dal nome, un notebook implementa la metafora di un quaderno su cui scrivere, in forma più o meno elegante. Nonostante siano stati pensati per un utilizzo fortemente orientato a contenuti di carattere matematico, i notebook di Mathematica si prestano anche a lavorare con informazione di tipo più eterogeneo. Ad esempio, quello che state leggendo è stato scritto utilizzando un notebook e non un impaginatore di testi. l notebook è il programma tramite cui un utente dialoga con il sistema, inserendo richieste e ottenendo risposte. Un'altra parte del sistema, chiamata kernel, si occupa invece di eseguire tutte le computazioni necessarie per portare a termine queste richieste, generando (eventualmente) le relative risposte. Seguendo la terminologia informatica, d'ora in avanti parleremo di input e di output piuttosto che di richieste e risposte. Più precisamente, ogni notebook è diviso in tante celle, ognuna tipicamente marcata da una parentesi quadra chiusa posta alla sua destra, che ne delimita il contenuto (e infatti ci si riferisce a questi componenti di Mathematica come a dei delimitatori di cella) e che permette di selezionare l'intera cella (cliccando sulla sua parte verticale), ad esempio per copiarla o cancellarla da un notebook. Un altro aspetto pratico dei delimitatori di cella è rappresentato dal fatto che le piccole barre orizzontali poste alle loro estremità permettono di individuare il punto in cui due celle si separano. Ciò è particolarmente utile quando si vuole inserire una nuova cella in mezzo a due celle esistenti: posizionando il cursore in un qualunque punto di un notebook compreso tra due delimitatori successivi, questo cambierà infatti la sua forma (assumendo quella di una linea orizzontale delimitata da due piccoli segmenti, in pratica l'equivalente ruotato del simbolo, detto caret o i-bean, che lo stesso cursore assume quando viene posizionato all'interno di un'area di testo in una qualunque interfaccia grafica), proprio a evidenziare il fatto che cliccando in questa posizione è possibile inserire una nuova cella. Quando si scrive qualcosa nella finestra di un notebook, viene automaticamente creata in esso una cella di input, cioé una cella in cui è possibile inserire dell'input da comunicare al kernel. Le celle di input sono riconoscibili perché la parte superiore del delimitatore di cella corrispondente è marcata con posizionando il cursore in un qualunque punto di un notebook compreso tra due delimitatori successivi, questo cambierà infatti la sua forma (assumendo quella di una linea orizzontale delimitata 2 da due piccoli segmenti, in pratica l'equivalente ruotato del simbolo, detto caret o i-bean, che lo stesso cap0.nb cursore assume quando viene posizionato all'interno di un'area di testo in una qualunque interfaccia grafica), proprio a evidenziare il fatto che cliccando in questa posizione è possibile inserire una nuova cella. Quando si scrive qualcosa nella finestra di un notebook, viene automaticamente creata in esso una cella di input, cioé una cella in cui è possibile inserire dell'input da comunicare al kernel. Le celle di input sono riconoscibili perché la parte superiore del delimitatore di cella corrispondente è marcata con un piccolo triangolo. Ogni notebook può poi implementare delle proprie regole di stile per formattare in modo opportuno celle di tipologia differente. Ad esempio le celle di input in questo notebook sono caratterizzate da un fondo arancione chiaro e da un font della famiglia Courier, che dovrebbe ricordare la forma con cui tipicamente gli elaboratori scrivono il codice. Questa è una cella di input Tramite le regole di stile è possibile modificare vari aspetti di una cella. In particolare è possibile disabilitare la visualizzazione dei delimitatori di cella. L'utente può scrivere in un notebook una generica espressione matematica e richiedere al kernel di valutarla. La valutazione avviene dopo che si è utilizzata una particolare combinazione di tasti (Û nella versione per Mac OS, ˜+Á nelle versioni per Linux o MS Windows) o la voce di menù Kernel/Evaluation/Evaluate cells. Il kernel riceve l'espressione, la valuta, e infine ritorna l'output relativo al notebook. Quest'ultimo si occupa di presentarlo in modo opportuno all'utente. Ad esempio In[208]:= Out[208]= 1+1 2 Attenzione: se è la prima volta che valutate una cella, il processo di valutazione potra' richiedere qualche secondo. Questo è dovuto al fatto che deve essere caricato ed eseguito un Kernel. L'output fornito da Mathematica appare in una cella il cui stile è molto simile a quello della cella che la precede, con l'unica differenza che lo sfondo ora è arancione scuro e il delimitatore ha un ulteriore linea orizzontale al di sotto del triangolo nella sua parte superiore. Questo sta a rimarcare il fatto che essa è appunto una cella di output. Nonostante sia possibile inserire in un notebook celle di altri tipi (ad esempio quello che state leggendo in questo istante è il contenuto di una cella di testo), per il momento noi ci occuperemo principalmente di celle contenenti input e output. Il lettore interessato può trovare in appendice alcuni approfondimenti relativi ad altri tipi di cella implementati. E' da notare come un gruppo di celle che siano in qualche modo correlate sia evidenziato da un ulteriore delimitatore, posto a destra rispetto a quelli delle singole celle, che delimita l'intero gruppo. Ciò succede ad esempio se andiamo ad esaminare una cella di input e la corrispondente (o le corrispondenti) celle di output. Gruppi di celle di questo tipo, oltre che creati automaticamente da Mathematica in opportune occasioni, possono essere creati dall'utente selezionando Tipicamente, la fase di immissione dei dati viene segnalata dalla pressione del tasto Á. In Mathematica invece tale tasto serve semplicemente per andare a capo in una cella. Un altro aspetto non standard è costituito dall'interpretazione degli spazi bianchi (caratteri di spazio, tabulazione e ritorno a capo). Questi in genere vengono ignorati e il loro uso permette di migliorare la leggibilità di quanto viene scritto. Mathematica invece li tratta nel modo seguente: cap0.nb 3 Tipicamente, la fase di immissione dei dati viene segnalata dalla pressione del tasto Á. In Mathematica invece tale tasto serve semplicemente per andare a capo in una cella. Un altro aspetto non standard è costituito dall'interpretazione degli spazi bianchi (caratteri di spazio, tabulazione e ritorno a capo). Questi in genere vengono ignorati e il loro uso permette di migliorare la leggibilità di quanto viene scritto. Mathematica invece li tratta nel modo seguente: gli spazi bianchi vengono ignorati; In[149]:= 1 Out[149]= 2 + 1 le tabulazioni non vengono accettate: provate a inserire una o più tabulazioni in una cella di input e vedrete che questa operazione non ha alcun effetto su quello che viene effettivamente scritto nella cella. Solitamente le tabulazioni vengono usate per indentare il codice scritto, una peculiarità che vedremo essere implementata automaticamente; i caratteri di "a capo" iniziano una nuova linea all'interno di una cella (senza segnalare, come detto prima, il termine dell'immissione dei dati). Va rimarcato il fatto che qualora una cella contenga più linee, queste verranno valutate separatamente e in sequenza dal kernel In[150]:= 1+1 1+2 1+3 Out[150]= 2 Out[151]= 3 Out[152]= 4 quindi questi caratteri non possono essere utilizzati per spezzare una riga troppo lunga: si consideri ad esempio la cella seguente In[153]:= 1 + 1 Out[153]= 1 Out[154]= 1 Mathematica ha interpretato il primo "1" come una riga a sé stante, mentre ha accorpato le due righe seguenti nell'espressione "+1", per poi valutare le due espressioni separatamente. Il fatto che le ultime due righe siano considerate come un'unica espressione è evidenziato dal fatto che la seconda tra queste è indentata rispetto alla prima. Mathematica spezza in modo generalmente automatico le linee troppo lunghe, tranne quando non riesce a trovare un punto adeguato in cui dividere il contenuto della cella. In questi casi in ogni 4 cap0.nb Mathematica ha interpretato il primo "1" come una riga a sé stante, mentre ha accorpato le due righe seguenti nell'espressione "+1", per poi valutare le due espressioni separatamente. Il fatto che le ultime due righe siano considerate come un'unica espressione è evidenziato dal fatto che la seconda tra queste è indentata rispetto alla prima. Mathematica spezza in modo generalmente automatico le linee troppo lunghe, tranne quando non riesce a trovare un punto adeguato in cui dividere il contenuto della cella. In questi casi in ogni linea viene scritto quanto più possibile, e la separazione è marcata a fine linea dal carattere speciale di separazione (Ö): In[158]:= 1111111111111111111111111111111111111111111111111111111111111111111111111Ö 111111111111111111111111111111111111111111111111111111 Il processo di valutazione è composto delle seguenti fasi: La cella in cui si trova il cursore viene etichettata (alla sua sinistra) dalla voce In[n], dove n è un numero che parte da 1 (quando viene eseguito il kernel) e aumenta progressivamente ogni volta che viene valutata una cella. Il contenuto di questa cella viene analizzato per verificare l'eventuale presenza di errori. In caso non ne vengano trovati, la cella viene valutata e l'eventuale risultato viene stampato in una nuova cella, automaticamente etichettata dalla voce Out[n], dove n è un numero progressivo calcolato nello stesso modo di In[n]. In caso vengano trovati degli errori, vengono stampati uno o più messaggi opportuni: se ad esempio scriviamo un'espressione matematica che non ha senso, come la seguente, otteniamo In[215]:= 3- * 2 Syntax::sntxf : "3 -" cannot be followed by " * 2". 3 - *2 Kernel e notebook sono due programmi separati che comunicano tra loro e che possono anche essere eseguiti separatamente.Ad esempio è possibile eseguire più notebook che facciano riferimento a uno stesso kernel,oppure eseguire un notebook su un computer e il relativo kernel su un altro,sfruttando una connessione di rete.Ancora,è possibile scindere una computazione complessa proveniente da un notebook in varie sotto-computazioni da fare eseguire in modo concorrente da altrettanti kernel in esecuzione su macchine differenti. 1.1.1. Modalità di inserimento per l'input Mathematica accetta come input una serie di oggetti dal significato differente, come ad esempio numeri interi, numeri irrazionali, testo, espressioni matematiche con un preciso stile (come ad esempio l'esecuzione di un'esponenziazione) e così via. Siccome non tutti questi oggetti trovano un corrispondente sulla tastiera di un computer (come la costante p), si rende necessario poterli inserire utilizzando delle modalità alternative di immissione dell'input. cap0.nb 5 Mathematica mette a disposizione le seguenti modalità di immissione: tramite tastiera, utilizzando una descrizione di tipo testuale. Questa rappresenta la scelta naturale per molti possibili input (i numeri interi, le quattro operazioni aritmetiche fondamentali, ...) i cui simboli corrispondenti trovano tipocamente posto nelle tastiere di un computer, mentre richiede il ricorso a una codifica opportuna per altre informazioni (ad esempio la costante "Pi greco" che viene indicata tramite la codifica Pi); va notato come questa codifica rimanga visualizzata sul notebook nella sua forma testuale; tramite tastiera, inserendo input di tipo complesso tramite un'opportuna codifica che viene immediatamente trasformata sul notebook in forma grafica; ad esempio è possibile visualizzare la costante "Pi greco" tramite l'usuale simbolo p premendo il tasto  (azione che viene confermata dalla comparsa sul notebook del carattere speciale Ç), inserendo la parola "pi" e premendo nuovamente il tasto Â: il notebook verrà aggiornato sostituendo i caratteri da Ç in avanti con il simbolo p. Per utilizzare questo tipo di modalità di immissione sono disponibili varie codifiche (denominate più precisamente alias), tra cui una parzialmente mutuata dal sistema LaTeX (un sistema di word processing in cui la descrizione dei documenti è fatta in modo puramente testuale, inserendo appunto dei comandi in un'opportuna codifica per indicare gli stili da applicare di volta in volta); tramite il puntatore del mouse e un sistema di palette, cioé di tabelle che contengono vari simboli che Mathematica inserisce nel notebook quando vengono selezionati. Per visualizzare (o nascondere) una palette, è sufficiente selezionare la voce di menù File/Palettes. In molti casi, quando il puntatore del mouse viene posizionato sopra un simbolo all'interno di una palette, nella parte inferiore di quest'ultima vengono visualizzate le codifiche per inserire il simbolo corrispondente tramite tastiera. 1.2. Espressioni aritmetiche La specificazione di espressioni aritmetiche segue più le convenzioni utilizzate più dai matematici che non dagli informatici. La differenza principale tra i due approcci sta nel fatto che non è necessario specificare alcun simbolo per indicare la moltiplicazione: basta inserire uno spazio tra i fattori In[227]:= Out[227]= 42 8 In ogni caso è possibile specificare esplicitamente l'operazione di moltiplicazione utilizzando il simbolo *. Questo è utile quando l'uso dello spazio può diminuire la leggibilità del codice Mathematica prodotto: In[228]:= Out[228]= 4*2 8 6 cap0.nb Le rimanenti operazioni aritmetiche vengono specificate utilizzando gli usuali simboli per indicare gli operatori di addizione (+) e sottrazione (-), a cui si aggiungono quelli relativi a divisione ( /), elevamento a potenza (^) e calcolo del fattoriale (!). In[229]:= Out[229]= In[230]:= Out[230]= In[232]:= Out[232]= In[231]:= 4+2 6 4-2 2 4ê2 2 4^2 Out[231]= 16 In[163]:= 4! Out[163]= 24 Ovviamente è possibile utilizzare le parentesi tonde per variare l'ordine di valutazione predefinito delle operazioni aritmetiche: In[63]:= Out[63]= 3 H4 + 5L 27 Il bello di utilizzare Mathematica sta nella possibilità di effettuare velocemente operazioni che richiederebbero troppo tempo se eseguite a mano, come ad esempio In[64]:= Out[64]= 2 ^ 100 1267650600228229401496703205376 cap0.nb In[166]:= Out[166]= 7 30 ! 265252859812191058636308480000000 1.3. Numeri interi ed approssimati Se proviamo a dividere 8 per 6 otteniamo come risultato In[237]:= Out[237]= 8ê6 4 ÅÅÅÅ 3 Mathematica ha quindi espresso il risultato della divisione come una frazione, che ha poi semplificato. In questo modo non viene introdotta alcuna approssimazione nel valore espresso. Analogamente si ha In[238]:= Out[238]= 5ê2 5 ÅÅÅÅ 2 Il risultato in questo caso è sempre espresso tramite una frazione, nonostante anche l'espressione decimale 2.5 fosse corretta. Mathematica è stato progettato per indicare i numeri in forma esatta, cioé senza introdurre approssimazioni, a meno che non gli venga esplicitamente richiesto di fare il contrario. Per i numeri decimali, la forma esatta viene indicata tramite la corrispondente frazione ridotta ai minimi termini: è per questo che il risultato della prima divisione è stato espresso come 4/3. Ogni volta che viene indicato un numero contentente un punto decimale (non necessariamente seguito da cifre), questo è inteso come espresso in forma approssimata, in cui tipicamente solo alcune delle potenzialmente infinite cifre significative vengono elencate. Anche questo è il motivo per cui il risultato della divisione precedente è stato espresso come 5/2 e non come 2.5. Quest'ultimo numero, infatti, potrebbe essere ad esempio confuso con l'espressione approssimata di 2.500000000000001. Ogni volta che un'espressione aritmetica contiene un numero decimale (eventualmente senza alcuna cifra dopo di esso), l'intera espressione viene valutata in modo approssimato In[242]:= Out[242]= 5 ê 2 + 1.5 4. E' da notare come il risultato di questa somma sia stato espresso con un punto decimale, proprio ad indicare che esso è approssimato, essendo approssimato uno degli addendi. Pertanto il risultato delle seguenti operazioni è differente: 8 cap0.nb In[243]:= Out[243]= In[244]:= Out[244]= 5ê2- 5ê2 0 5 ê 2 - 2.5 0. Il primo risultato è la differenza tra due numeri uguali, entrambi espressi in forma esatta, che pertanto è uguale a zero (in forma esatta). Il secondo è la differenza tra due numeri uguali, dei quali uno è espresso in forma approssiamata, che sarà quindi uguale a zero in forma approssimata, come indicato dal punto decimale. Mathematica elabora in modo molto diverso i numeri espressi in forma esatta e quelli espressi in forma approssimata. In particolare, la valutazione di espressioni complesse contenenti numeri in forma approssimata richiede un tempo tipicamente inferiore rispetto ai numeri in forma esatta. Tenere a mente questa differenza può portare pertanto a realizzare modelli con tempi di esecuzione ottimizzati. 1.4. Costanti Una costante rappresenta un valore che non varia nel tempo. Si trovano vari esempi di costanti in fisica (la velocità della luce, la costante di graviazione universale, ...) e in matematica (il pi greco, ma anche un qualsiasi numero). Mathematica implementa, tra le altre, le seguenti costanti matematiche: il pi greco (la lunghezza di una circonferenza di diametro unitario): In[3]:= Out[3]= Pi p la costante di Nepero (il valore della somma ‚ In[4]:= E Out[4]= ‰ L'unità immaginaria (la radice quadrata di -1): In[5]:= I Out[5]=  +¶ ÅÅÅÅ1ÅÅ n=0 n! ): cap0.nb 9 In realtà sono implementate anche molte altre costanti di tipo matematico e fisico, ma non le prenderemo in considerazione. 1.5. Funzioni Le funzioni vengono specificate indicando il loro nome seguito da una coppia di parentesi quadre che ne racchiudono gli argomenti. In un certo senso le costanti possono essere pensate come delle funzioni che non hanno argomenti (anche se vedremo che ricorrendo alla generazione di numeri pseudo-casuali è possibile utilizzare Mathematica per costruire delle funzioni senza argomenti che non realizzano delle costanti). Nel seguito vedremo un breve elenco che illustra come Mathematica possa calcolare alcune tra le funzioni matematiche più semplici. In realtà il sistema implementa un numero decisamente più elevato di funzioni, di complessità anche elevata, che noi non tratteremo. Il lettore interessato ad approfondire questo argomento può consultare The Mathematica Book di Steven Wolfram. 1.5.1. Funzioni logaritmiche ed esponenziali La funzione Log[x] indica il logaritmo naturale di x: In[7]:= Out[7]= Log@E ^ 4D 4 Se la funzione Log viene chiamata usando due argomenti, allora il primo rappresenta la base del logaritmo e il secondo il suo argomento: In[9]:= Log@2, 8D Out[9]= 3 In[93]:= Log@10, 1000000D Out[93]= 6 E' anche disponibile la funzione esponenziale Exp[x], nonostante in fase di valutazione venga direttamente convertita in ‰^x: In[94]:= Out[94]= Exp@2D ‰2 10 cap0.nb 1.5.2. Radicali La funzione Sqrt determina le radici di un numero. In particolare, se usata con un solo argomento ne calcola la radice quadrata In[95]:= Out[95]= Sqrt@16D 4 Il calcolo di radici di altro tipo viene effettuato tenuto conto del fatto che possibile calcolare la radice cubica di 8 come In[98]:= Out[98]= b ! è!!! x =x 1êb . Ad esempio è 8 ^ H1 ê 3L 2 Mathematica mette a disposizione un simbolo grafico per indicare in forma tradizionale le radici. Quando un'espressione che lo contiene viene valutata, il simbolo viene convertito nelle espressioni sopra indicate, come possiamo verificare utilizzando la funzione InputForm (di cui vedremo tra breve il significato): In[99]:= è!!! InputFormA x E Out[99]//InputForm= Sqrt[x] In[100]:= b è!!! InputFormA x E Out[100]//InputForm= x^b^(-1) Attenzione! Non fatevi ingannare da quest'ultima espressione, che va letta come x^(b^(-1)) a causa delle regole di precedenza nella valutazione degli operatori. Per convincervene, provate per esercizio a valutare separatamente, ad esempio, le espressioni 8^(1/3), 8^3^(-1) e (8^3)^(-1). 1.5.3. Funzioni trigonometriche Le funzioni Sin, Cos e calcolano rispettivamente il seno, il coseno e la tangente del loro argomento cap0.nb 11 In[104]:= Out[104]= In[105]:= Out[105]= Sin@Pi ê 4D 1 ÅÅÅÅÅÅÅÅÅ è!!! 2 Cos@Pi ê 3D 1 ÅÅÅÅ 2 Mathematica implementa direttamente anche altre funzioni trigonometriche esprimibili in termini di seno e coseno; ad esempio la funzione Tan calcola la tangente dell'argomento che le viene passato: In[108]:= Out[108]= Tan@Pi ê 3D è!!! 3 Mathematica semplifica ove possibile le espressioni trigonometriche: In[112]:= Out[112]= Sin@xD ê Cos@xD Tan@xD Inoltre sono implementate le funzioni trigonometriche inverse ed esponenziali: ArcSin e ArcCos calcolano rispettivamente l'inverso delle funzioni seno e coseno In[118]:= Out[118]= In[123]:= Out[123]= ArcSin@1D p ÅÅÅÅ 2 ArcCos@Sqrt@2D ê 2D p ÅÅÅÅ 4 Sinh e Cosh calcolano rispettivamente il seno e coseno iperbolici, mentre ArcSinh e ArcCosh indicano le corrispondenti funzioni inverse: 12 cap0.nb In[136]:= Out[136]= In[138]:= Out[138]= Sinh@0D 0 Cosh@0D 1 1.5.4. Altre funzioni Le funzioni Abs e Sign ritornano rispettivamente il valore assoluto e il segno di un numero (cioé -1 o 1 a seconda che il rispettivo argomento sia o meno negativo) In[14]:= Out[14]= In[15]:= Out[15]= In[16]:= Out[16]= In[17]:= Out[17]= Abs@4D 4 Abs@-4D 4 Sign@4D 1 Sign@-4D -1 Attenzione! Le funzioni Abs e Sign assumono un significato differente quando i loro argomenti sono numeri complessi. In questo caso Abs[z] corrisponde al modulo di z e Sign[z] è uguale a z/Abs[z]. In[13]:= Out[13]= Sign@3 - ID 3- ÅÅÅÅÅÅÅÅ ÅÅÅÅ è!!!!!! 10 cap0.nb 13 La funzione Round arrotonda il suo argomento all'intero più vicino In[18]:= Out[18]= In[19]:= Out[19]= [email protected] 3 [email protected] 4 Le funzioni IntegerPart e FractionalPart ritornano rispettivamente le parti intera e frazionaria di un numero reale In[26]:= Out[26]= In[27]:= Out[27]= In[28]:= Out[28]= In[29]:= Out[29]= [email protected] 4 [email protected] 0.2 [email protected] -4 [email protected] -0.2 Attenzione! Se l'argomento di FractionalPart è un numero irrazionale espresso in forma esatta, il risultato sarà anch'esso espresso in questa forma. In[30]:= Out[30]= FractionalPart@PiD -3 + p La funzione Floor ritorna il più grande intero minore o uguale dell'argomento specificato; analogamente, la funzione Ceiling ritorna il più piccolo intero maggiore o uguale dell'argomento specificato 14 cap0.nb In[31]:= Out[31]= In[32]:= Out[32]= In[33]:= Out[33]= In[34]:= Out[34]= Floor@PiD 3 Ceiling@PiD 4 Floor@-PiD -4 Ceiling@-PiD -3 Le funzioni Quotient e Mod ritornano rispettivamente il quoziente e il resto della divisione intera tra il primo e il secondo argomento specificato In[35]:= Out[35]= In[36]:= Out[36]= Quotient@10, 4D 2 Mod@10, 4D 2 1.6. Notazioni di presentazione degli argomenti La notazione tipicamente utilizzata per calcolare una funzione viene indicata in una notazione prefissa in cui il nome della funzione precede gli argomenti, delimitati in modo opportuno (da parentesi tonde nell'usuale notazione matematica, da parentesi quadre nella sintassi di Mathematica in forma input o standard). In realtà è possibile fare riferimento ad altre modalità: una notazione postfissa, utilizzabile con funzioni che hanno un solo argomento. In questo caso viene specificato prima l'argomento, poi due caratteri di barra capovolta o backslash ( \\) e infine il nome della funzione cap0.nb In[224]:= Out[224]= 15 x êê Log Log@xD questo tipo di notazione risulta indicata per scrivere velocemente un'espressione, quando non si vuole appesantire un'espressione già complessa con un'ulteriore coppia di parentesi quadre, o quando si vuole modificare una cella di input usandone il contenuto come unico argomento di qualche funzione. In quest'ultimo caso è sufficiente aggiungere in coda all'espressione il doppio backslash e il nome della funzione; una notazione infissa, utilizzabile con funzioni che hanno due argomenti. In questo caso viene specificato innanzitutto il primo argomento, seguito da un carattere di tilde ( ~), dal nome della funzione, da un ulteriore carattere di tilde e dal secondo argomento In[228]:= Out[228]= 10 ~ Log ~ 1000 3 una forma alternativa della notazione prefissa per funzioni con un solo argomento, in cui si indicano semplicemente il nome della funzione e l'argomento separandoli tramite un carattere di "at" (@) In[231]:= Out[231]= Log ü x Log@xD questo tipo di forma permette di scrivere velocemente un'espressione, eliminando il bisogno delle parentesi quadre. In generale queste notazioni e le relative abbreviazioni sono state introdotte per rendere più agevole l'inserimento di espressioni in una cella. In realtà è necessario, di volta in volta, decidere se utilizzarle o meno anche tenendo conto della misura in cui il loro utilizzo possa andare a migliorare o peggiorare la leggibilità delle stesse espressioni. 1.7. Forme di visualizzazione dei dati Consideriamo l'espressione I^Cos[x], indicante cioé il numero complesso ottenuto elevando l'unità immaginaria al coseno di x. Tale espressione è espressa utilizzando le regole sintattiche di Mathematica (quelle che stiamo gradualmente imparando), le quali si basano su due caratteristiche importanti: permettono di descrivere una qualunque espressione matematica in modo non ambiguo; possono essere descritte utilizzando esclusivamente una descrizione puramente testuale (nel senso che il contenuto che esse esprimono è espresso dai caratteri utilizzati e non dal loro stile). 16 cap0.nb Le espressioni indicate utilizzando questa sintassi vengono dette espressioni in Input Form (o forma di input); Mathematica implementa la funzione InputForm per convertire espressioni in questa forma: In[198]:= I ^ Cos@xD êê InputForm Out[198]//InputForm= I^Cos[x] Nonostante la forma di input rappresenti un'interfaccia tramite cui inserire velocemente espressioni matematiche all'interno di un notebook, la sua lettura può risultare difficile soprattutto quando non se ne conosce bene la sintassi. Mathematica è però in grado di esprimere i dati utilizzando altre forme, tant'è che se proviamo a valutare I+Log[x] otteniamo In[199]:= Out[199]= I ^ Cos@xD ÂCos@xD e quindi una cella di output in cui la seconda delle caratteristiche sopra indicate non è più soddisfatta: in questo caso infatti l'unità immaginaria è indicata non più tramite la lettera I (che in particolare, essendo una "I" maiuscola rappresenta un carattere ben preciso), bensì tramite il simbolo  ottenuto applicando uno stile ben preciso (quello che Mathematica indica come il font di tipo Double Struck) al carattere i; l'operazione di elevamento a potenza viene indicata non più dal carattere ^ ma scrivendo l'esponente come apice della alla base. Attenzione! Non fatevi ingannare dal fatto che fino ad ora abbiamo indicato l'unità immaginaria utilizzando in ogni caso uno stile differente (e cioé applicando alla lettera maiuscola "I" il font di tipo Courier): questo è dovuto al fatto che tutti i caratteri di un'espressione in forma di input sono indicati con questo stile, in modo che queste espressioni risultino visualmente differenziate dal testo. Mathematica utilizza quindi una seconda forma di presentazione dei dati (tipicamente per le celle di output), detta Standard Form o forma standard, in cui mantiene l'univocità della descrizione ma, per migliorare la leggibilità, utilizza in alcuni casi la formattazione del testo per indicare un significato particolare: ad esempio per indicare alcune costanti (come I ed E, che vengono rispettivamente trasformate in  ed ‰) o alcune operazioni (come l'estrazione di radice o l'elevamento a potenza) che vengono indicate nell'usuale modo tipografico. La funzione StandardForm permette di convertire un'espressione in forma standard cap0.nb In[200]:= 17 I ^ Cos@xD êê StandardForm Out[200]//StandardForm= ÂCos@xD Siccome la forma standard mantiene il requisito di descrivere un'espressione in modo univoco, è possibile utilizzarla per inserire input in una cella In[197]:= Out[197]= !2 ! 3 !5 Mathematica implementa un'ulteriore forma, detta Traditional Form o forma tradizionale, in cui i dati vengono presentati in modo molto simile all'usuale notazione matematica: ad esempio le funzioni vengono indicate utlizzando caratteri minuscoli, le variabili utilizzando lo stile corsivo e gli argomenti di una funzione sono delimitati da parentesi tonde e non quadre. La funzione TraditionalForm permette di esprimere un'espressione in forma tradizionale, evidenziata anche da una linea verticale a zig-zag nel corrispondente delimitatore di cella: In[201]:= I ^ Cos@xD êê TraditionalForm Out[201]//TraditionalForm= ÂcosHx L Va notato come la notazione in forma tradizionale possa risultare ambigua da interpretare. Ad esempio l'espressione a(x+1) può indicare sia il valore che la funzione a assume in corrispondenza dell'argomento x+1 che il prodotto tra i fattori a e x+1. In[212]:= a@x + 1D êê TraditionalForm Out[212]//TraditionalForm= aHx + 1L In[213]:= a Hx + 1L êê TraditionalForm Out[213]//TraditionalForm= a Hx + 1L Attenzione! E' possibile modificare la forma di presentazione di una cella anche selezionandone il corrispondente delimitatore e scegliendo una delle voci del menù Cell/ConvertTo. 18 cap0.nb Tenuto conto di questo, Mathematica implementa input e standard form in modo da risultare il più possibile consistente. Il lettore interessato ad approfondire questo concetto può fare riferimento ai primi capitoli del libro Exploring Mathematics with Mathematica, di John Glynn e Theodore Gray. Mathematica implementa in realtà anche altre forme di rappresentazione dei dati: alcune, come MatrixForm o Short, verranno introdotte nel seguito quando si renderanno necessarie; altre, come TeXForm, non verranno introdotte. Il lettore interessato ad approfondire questo argomento può consultare il manuale in linea. 1.8. Approssimazione di numeri Quando si prova a calcolare il logaritmo naturale di 8 si ottiene In[10]:= Log@8D Out[10]= Log@8D l'output è esattamente uguale all'input, perché il logaritmo naturale di 8 non è né un numero relativo né un numero razionale, ma un numero reale non esprimibile in forma esatta se non come il logaritmo naturale di 8. Per visualizzare questo numero in forma approssimata possiamo indicare il suo argomento in forma approssimata, aggiungendovi il punto decimale: In[38]:= [email protected] Out[38]= 2.07944 inserirlo in un'espressione matematica in cui compaia almeno un numero in forma approssimata: In[40]:= Out[40]= Log@8D + 0.0 2.07944 Utilizzare la funzione N che, avuto come argomento un numero, calcola il suo valore approssimato: In[36]:= Out[36]= N@Log@8DD 2.07944 Utilizzando la funzione N è possibile calcolare approssimazioni molto precise: infatti è possibile fornire un secondo argomento che permette di specificare il numero di cifre significative da indicare nell'approssimazione cap0.nb In[89]:= Out[89]= 19 N@Log@8D, 100D 2.07944154167983592825169636437452970422650040308076576236204002848018086Ö 5909084146817589980989256063 In questo modo è ad esempio possibile scrivere approssimazioni molto accurate di p, ad esempio fino alla centesima cifra: In[90]:= Out[90]= N@p, 100D 3.14159265358979323846264338327950288419716939937510582097494459230781640Ö 6286208998628034825342117068 o alla millesima In[91]:= Out[91]= N@p, 1000D 3.14159265358979323846264338327950288419716939937510582097494459230781640Ö 628620899862803482534211706798214808651328230664709384460955058223172535Ö 940812848111745028410270193852110555964462294895493038196442881097566593Ö 344612847564823378678316527120190914564856692346034861045432664821339360Ö 726024914127372458700660631558817488152092096282925409171536436789259036Ö 001133053054882046652138414695194151160943305727036575959195309218611738Ö 193261179310511854807446237996274956735188575272489122793818301194912983Ö 367336244065664308602139494639522473719070217986094370277053921717629317Ö 675238467481846766940513200056812714526356082778577134275778960917363717Ö 872146844090122495343014654958537105079227968925892354201995611212902196Ö 086403441815981362977477130996051870721134999999837297804995105973173281Ö 609631859502445945534690830264252230825334468503526193118817101000313783Ö 875288658753320838142061717766914730359825349042875546873115956286388235Ö 3787593751957781857780532171226806613001927876611195909216420199 Attenzione! Nel caso vi facciate prendere la mano, può capitare che chiediate a Mathematica di effettuare delle computazioni che richiedono un tempo troppo elevato per essere portate a termine. In casi come queste potete terminare in anticipo la computazione utilizzando la voce di menù Kernel/Abort Evaluation, o la combinazione di tasti CTRL-. Le funzioni possono venire specificate, oltre che nella modalità seguita finora (detta forma prefissa, in quanto il nome della funzione viene specificato prima dei suoi argomenti), anche in altre forme. In particolare è disponibile una forma postfissa per le funzioni che hanno un solo argomento, che consiste nello specificare prima l'argomento stesso, seguito da un doppio slash (//) e dal nome della funzione. In altre parole 20 cap0.nb In[92]:= Out[92]= p êê N 3.14159 ha lo stesso effetto di N[p]. Questa forma risulta spesso utile per migliorare la leggibilità di formule molto lunghe in quanto evita di dover specificare un ulteriore coppia di parentesi quadre. 1.9. Espressioni Un'espressione di Mathematica è definita nel seguente modo: una costante (compreso un numero, in forma esatta o approssimata) è un'espressione; se f è una funzione con un argomento e x è un'espressione che rappresenta un argomento valido per f, allora f[x] è un'espressione. Analogamente, se g è una funzione con n argomenti e x1, ..., xn sono n espressioni che rappresentano argomenti validi per g (nel senso che x1 è un'espressione che può rappresentare il primo argomento di g, x2 è un'espressione che può rappresentare il suo secondo argomento e così via), allora g[x1,...,xn] è un'espressione. L'ultimo dei due punti precedenti si estende facilmente al caso di operatori cioé funzioni, come la somma, che vengono tipicamente utilizzati tramite una notazione prefissa o infissa, in cui il simbolo che descrive l'operatore viene inserito in una posizione particolare e gli argomenti non vengono delimitati tramite parentesi. Limitandosi agli operatori unari e binari (aventi cioé uno o due argomenti): se U è un operatore unario e x è un'espressione che può essere considerata un argomento valido per U, allora Ux è un'espressione: ad esempio -x è un'espressione valida, dove x è un numero e - indica l'operatore unario di negazione; se B è un operatore binario e x ed y sono due espressioni che possono essere considerate argomenti validi per B, allora xBy è un'espressione; ad esempio se x ed y sono due numeri e + indica la somma, allora x+y è un'espressione valida. Merita maggiore attenzione il concetto di argomento valido: normalmente a ogni dato è associato un tipo che ne riassume la natura e le caratteristiche (sono ad esempio tipi diversi i numeri in forma esatta, quelli in forma approssimata, le parole). Ogni funzione è progettata per accettare argomenti di un tipo ben definito, o di un definito insieme di tipi. Ad esempio la funzione Log accetta come argomenti numeri, in forma esatta o approssimata, ma non avrebbe alcun senso pensare di calcolare il logaritmo della parola "ciao" o della data odierna (e più avanti vedremo come Mathematica possa trattare anche questi tipi di dati). Attenzione! In realtà Mathematica implementa gli operatori esattamente come se fossero delle funzioni: ad esempio l'espressione x+y viene convertita in Plus[x,y] prima di venire inviata al kernel, e la stessa conversione viene fatta in senso opposto per i risultati che il kernel fornisce al notebook. cap0.nb 21 Quando un'espressione contiene più funzioni innestate (ovvero funzioni in cui almeno un argomento è rappresentato dal valore assunto da un'altra funzione), queste vengono valutate nell'unico modo possibile, cioé partendo da quella più interna a quella più esterna: ad esempio nell'espressione f@x, g@h@y, zDDD le funzioni verranno valutate nel seguente ordine: prima verrà valutata h quando y e z sono rispettivamente il suo primo e secondo argomento; poi verrà valutata g utilizzando questo valore come argomento; infine verrà valutata f utilizzando questo valore come secondo argomento e x come primo argomento. Quando invece in un'espressione compaiono più operatori, come ad esempio in x+y/z non è chiaro se debba prima essere valutato l'operatore di divisione o quello di addizione. Per questo motivo si rendono necessarie delle regole di precedenza che indicano quale debba essere l'ordine di valutazione in casi come questo. Nell'ordine di valutazione implementato da Mathematica le operazioni che abbiamo visto finora sono valutate secondo le usuali regole di precedenza: fattoriale elevamento a potenza prodotto e quoziente somma e sottrazione E' possibile forzare un diverso ordine di valutazione degli operatori utilizzando le parentesi tonde: In[258]:= Out[258]= In[259]:= Out[259]= x +yêz y x + ÅÅÅÅ z Hx + yL ê z x+y ÅÅÅÅÅÅÅÅÅÅÅÅ z 1.10. Sostituzioni Codice la cui forma generale è 8prima Ø dopo< indica una sostituzione. L'operatore /. permette di applicare una sostituzione a una qualunque espressione 22 cap0.nb In[190]:= Out[190]= a + b ^ 2 + b c ê. 8b Ø 1 ê Pi< 1 c a + ÅÅÅÅÅÅ Å + ÅÅÅÅ p2 p Le sostituzioni sono tipicamente legate al modo in cui Mathematica presenta i risultati di equazioni. 1.11. Variabili Nella costruzione di modelli matematici complessi risulta utile spezzare una computazione compelssa in calcoli relativamente semplici. aumentare la leggibilità del codice; generare un risultato una sola volta per riutilizzarlo in molte occasioni; generare un modello matematico in modo incrementale. Per potere però utilizzare i risultati di queste computazioni è necessario che questi vengano salvati una volta prodotti. Mathematica implementa questa funzionalità ricorrendo al concetto di variabile. Tipicamente le variabili vengono viste come controparte dinamica delle costanti: infatti, mentre una costante rappresenta un oggetto tramite cui utilizzando un nome simbolico ci si può riferire a un valore che non varia nel tempo, utilizzando una variabile si utilizza sempre un nome simbolico, ma per riferirsi a un valore che può variare nel tempo. Quindi una variabile è accessibile tramite un nome, scelto dall'utente che la crea. Un nome valido è costituito da una successione di caratteri di arbitraria lunghezza che non inizi con un numero, tenendo conto che il linguiaggio di Mathematica è case sensitive: un carattere minuscolo e il corrispettivo maiuscolo sono considerati diversi; quindi sono ad esempio nomi validi di variabili variabile, Variabile, VARIABILE, vArIAbiLe, variabile1, unaVariabile e unAltraVariabile. In realtà , nonostante tutti questi nomi possano essere utilizzati, nella pratica si tiene conte delle cosiddette regole di stile, in base a cui: i nomi di una variabile composti da una sola parola sono scritti utilizzando esclusivamente lettere minuscole. Nomi che iniziano con una lettera maiuscola sono riservati per costanti e funzioni già implementate in Mathematica; i nomi di una variabile composti da più parole sono scritti accorpando le varie parole, scritte con caratteri minuscoli ma utilizzando i caratteri maiuscoli per le iniziali di ogni parola. Unica eccezione è costituita dalla prima parola, che è composta esclusivamente da caratteri minusoli. Quindi solo il primo e gli ultimi tre tra i nomi sopra elencati rappresentano nomi validi per una variabile. Il carattere di uguale (=) viene utilizzato per indicare l'operatore di assegnamento che associa un valore a una variabile. La sua sintassi è la seguente nomeVariabile = < espressione > cap0.nb 23 dove nomeVariabile è un nome valido per una variabile e <espressione> è un'espressione valida. Durante l'esecuzione di questa oerazione, l'espressione viene valutata ed il suo risultato viene assegnato alla variabile. Ad esempio l'assegnamento In[261]:= Out[261]= a = 4+2 6 ha come esito l'assegnamento di 6 (il risultato di 4+2) alla variabile a. Si noti che il valore assegnato è anche l'output corrispondente dell'operazione. Per verificare che l'assegnamento abbia effettivamente avuto l'esito voluto possiamo valutare il valore di a: In[264]:= a Out[264]= 6 infatti ogni nome di variabile, al pari delle costanti, rappresenta un'espressione valida. Siccome tipicamente quando un valore viene memorizzato in una variabile non è necessario visualizzarlo, è possibile inserire un carattere di punto e virgola (;) in coda all'operazione: In[273]:= a = 4 + 2; In questo modo l'assegnamento viene eseguito senza generare alcun output. Mathematica può in realtà generare due tipi diversi di output: quello che va a comparire nelle celle di output, viene emesso quando valutando una cella questa produce uno o più risultati. Questo tipo di output viene generato dalla maggior parte delle istruzioni implementate da Mathematica, ovviamente a meno che la relativa visualizzazione non sia disabilitata tramite l'uso del carattere di punto e virgola; E' importante tenere a mente come questo tipo di output faccia riferimento a delle informazioni che possono essere memorizzate e/o rielaborate in altre parti del codice. quello il cui scopo è semplicemente il mostrare nel notebook un messaggio di qualche tipo, che non viene poi utilizzato per ulteriori computazioni; in altre parole, il concetto di output tipicamente usato dai programmatori per notificare delle informazioni a un utente. L'outupt di questo tipo viene generato dall'istruzione Print, il cui argomento è un'espressione che viene valutata e stampata. In[93]:= Print@Pi ^ 3D p3 24 cap0.nb Vale la pena di notare come l'output di un'istruzione Print, seppur contenuto in una cella di output, non abbia dato origine all'emissione di un'etichetta Out, proprio perché non vi è stata alcuna memorizzazione dell'output stesso. Per evitare confusione, utilizzeremo la parola output per indicare la prima modalità e la parola messaggio per indicare la seconda. Attenzione! Rimarchiamo un'interessante coincidenza: come abbiamo già detto, la maggior parte delle istruzioni di Mathematica danno origine a un output. Questo significa che alcune istruzioni vengono eseguite senza generare un output. Un esempio di queste istruzioni è rappresentato appunto da Print, che non produce alcun output ma genera un messaggio. Un altro esempio si può trovare nella funzione Clear, che vedremo tra poco: questa funzione non produce né output né messaggi. Una volta assegnato un valore ad una variabile, questa è utilizzabile per eseguire altre computazioni: hanno senso ad esempio le seguenti espressioni In[279]:= Out[279]= In[281]:= Out[281]= a+1 6 Log@aD êê N 1.60944 In linea con i moderni linguaggi di programmazione, Mathematica mette a disposizione una serie di operatori che permettono di abbreviare delle operazioni matematiche di uso comune: l'operatore di autoincremento, indicato da un doppio segno di addizione ( ++), che applicato a una variabile ne incrementa il valore di un'unità. Questo operatore può essere utilizzato sia in notazione prefissa che in notazione postfissa. Pertanto sia ++a che a++ equivalgono all'assegnamento a=a+1, ma avranno un comportamento differente qualora queste istruzioni siano contenute in un'espressione. In particolare, qualora un'espressione contenga un'istruzione di autoincremento prefisso, questa viene eseguita prima di valutare l'espressione: ad esempio In[19]:= Out[20]= a = 5; ++a ê 2 3 In questo caso, la variabile a viene inizializzata al valore 5 e poi viene valutata l'espressione ++a/2. Siccome questa espressione contiene un'autoincremento prefisso, prima il contenuto di a viene incrementato di un'unità (assumento pertanto il valore 6), e poi viene valutata l'espressione a/2, che appunto vale 3. Per conferma, possiamo verificare quale sia il valore contenuto in a: cap0.nb 25 In[21]:= a Out[21]= 6 Quando invece l'espressione contiene un'istruzione di autoincremento postfisso, quest'ultima viene eseguita solo dopo che l'espressione è stata valutata: a = 5; a ++ ê 2 In[22]:= 5 ÅÅÅÅ 2 Out[23]= infatti, dopo l'inizializzazione a 5, viene prima valutata l'espressione a/2 (che appunto vale 5/2) e poi il valore di a viene incrementato, come è facilmente verificabile anche in questo caso: In[24]:= a Out[24]= 6 l'operatore di autodecremento, indicato da un doppio segno di differenza ( --), utilizzabile nelle stesse modalità dell'operatore di autoincremento, che ha come effetto quello di diminuire il contenuto di una variabile di un'unità: a = 5; --a In[27]:= 4 Out[28]= gli operatori di incremento e decremento (una generalizzazione degli operatori di autoincremento e autodecremento), denotati rispettivamente con += e -=, che permettono di aggiungere o sottrarre una data quantità al contenuto di una variabile. In altre parole, a+=x equivale ad a=a+x e a-=x equivale ad a=a-x. a = 1; a += 2; Print@aD; In[1]:= 3 Infine, è possibile "cancellare" una variabile, come se non le avessimo mai assegnato alcun valore, utilizzando la funzione Clear: 26 cap0.nb In[29]:= a = 5; Clear@aD In[31]:= a Out[31]= a Attenzione! E' importante ricordare che Mathematica gestisce le variabili dando loro una visibilità globale , nel senso che, una volta definita, una variabile può essere letta e modificata in qualunque altra parte del notebook, o anche in altri notebook. Nel caso capiti che in parti diverse del codice scritto compaiano due variabili con scopi differenti ma stesso nome, la modifica del valore di una di queste si rifletterà anche sull'altra, con potenziali danni sul corretto funzionamento del codice. E' quindi importante sforzarsi di dare nomi differenti a variabili differenti, tipicamente attribuendo dei nomi che ricordino il significato per cui ogni variabile è utilizzata. In realtà è possibile, in alcuni casi, fare anche riferimento a variabili di tipo locale, la cui visibilità è ristretta a una ben precisa porzione di codice su cui avranno anche effetto le modifiche apportate ai suoi contenuti. 1.12. Costruire nuove funzioni Un'importante funzionalità di Mathematica consiste nella possibilità di estendere il suo linguaggio aggiungendo delle funzioni definite dall'utente. Tale possibilità si basa sull'operatore di valutazione posticipata :=, la cui sintassi nome:=espressione viene interpretata nel modo seguente: ogni qual volta nel codice seguente si farà riferimento a nome, verrà valutata espressione. Ad esempio, il codice In[12]:= multiploPi := n Pi; definisce multiploPi come valore l'espressione pari a n volte p, dove n è una variabile il cui valore verrà valutato ogni volta che si farà riferimento a multiploPi: In[12]:= Out[13]= In[14]:= Out[15]= n = 1; multiploPi p n = 3; multiploPi 3p In realtà l'utilizzo della variabile n in questo caso è potenzialmente scorretto, in quanto potremmo non essere sicuri che altre parti di codice non facciano riferimento ad essa, modificando implicitamente la definizione di multiploPi. Per evitare inconvenienti di questo tipo e simultaneamente implementare più correttamente una funzione come una corrispondenza tra argomenti e valori, Mathematica utilizza la seguente sintassi cap0.nb 27 funzione@argomento_D := espressione dove funzione è il nome scelto per la funzione; := rappresenta l'operatore di valutazione posticipata; espressione rappresenta l'espressione che verrà valutata per produrre il valore della funzione; argomento è il nome per l'argomento della funzione. Il carattere di underscore ( _) sta appunto ad indicare che, quando si farà riferimento a questa nuova funzione specificando un argomento, il valore di quest'ultimo verrà valutato e inserito all'interno di espressione in corrispondenza di ogni occorrenza dell'insieme di caratteri argomento. Solo dopo questa operazione il sistema provvederà a valutare espressione e a ritornare un valore. Quindi la funzione multiploPi può venire implementata più correttamente nel seguente modo: In[133]:= multiploPi@n_D := n p e valutata indicando i valori attuali del suo argomento tra parentesi quadre: In[134]:= Out[134]= In[135]:= Out[135]= multiploPi@1D p multiploPi@7D 7p Attenzione! Se state eseguendo questo notebook, il sistema potrebbe emettere un errore quando valuta la definizione di multiploPi, se avevate anche valutato le cella a inizio di questa sezione. Infatti in questo caso il nome multiploPi è già stato associato a un altro valore. Per poter valutare queste ultime celle sarà quindi necessario eliminare l'associazione tramite il comando Clear. Nel caso una funzione abbia più di un argomento, la sintassi della definizione non cambia, a patto di indicare tutti gli argomenti all'interno delle parentesi quadre separandoli tramite il carattere di virgola: In[137]:= logAllaEnne@x_, n_D := Log@xD ^ n 28 cap0.nb 1.13. Vincoli sugli argomenti di una funzione 1.13.1. Argomenti opzionali E' possibile definire un argomento di una funzione come opzionale, facendo seguire il relativo carattere di underscore dal carattere di due punti (:) e dal valore predefinito da assegnare all'argomento qualora questo non venga specificato: In[156]:= cosAllaEnne@x_, n_: 2D := Cos@xD ^ n In[157]:= cosAllaEnne@x, 3D Out[157]= In[158]:= Out[158]= Cos@xD 3 cosAllaEnne@xD Cos@xD 2 1.13.2. Argomenti tipizzati E' possibile specificare il tipo di un argomento facendo seguire il relativo carattere di underscore da Integer, Real, o Complex, per indicare rispettivamente che l'argomento deve essere di tipo intero, reale o complesso. Ad esempio possiamo definire la funzione fattoriale solo per argomenti interi: In[159]:= fattoriale@n_IntegerD := n ! in questo modo la funzione si comporta nel modo usuale quando chiamata con un argomento intero: In[160]:= Out[160]= fattoriale@3D 6 se invece si utilizza un qualunque numero reale come argomento la funzione rimane non computata, non essendone stato definito il comportamento in questi casi: cap0.nb In[161]:= Out[161]= 29 fattoriale@PiD fattoriale@pD Va notato come il seguente comportamento della funzione In[162]:= [email protected] Out[162]= [email protected] sia in realtà perfettamente consistente, dal momento che 3. è considerato un numero reale. 1.13.3. Argomenti vincolati E' possibile specificare per gli argomenti di una funzione dei vincoli più complessi di quelli introdotti nelle sezioni precedenti, facendo seguire il relativo carattere di underscore dalla sequenza di caratteri /; e da una condizione. Ad esempio la definizione della funzione fattoriale vista nella precedente sezione risulta mal definita se l'argomento, seppure intero, è negativo. In[172]:= Out[172]= fattoriale@-4D ComplexInfinity Utilizzando /; è possibile fare in modo che la definizione venga applicata solo in caso di argomenti positivi: In[168]:= fattoriale2@n_ ê; n > 0D := n ! In[173]:= fattoriale2@4D Out[173]= 24 Nel caso l'argomento fornito sia negativo, la funzione non verrà valutata In[171]:= fattoriale2@-4D Out[171]= fattoriale2@-4D 30 cap0.nb Alternativamente l'operatore /; può essere inserito dopo quello di valutazione posticipata, a indicare che l'applicazione di quest'ultimo è condizionata al soddisfacimento di una condizione. Ad esempio la funzione fattoriale può essere definita come In[174]:= fattoriale3@n_D := n ! ê; n > 0 Naturalmente i vari tipi di vincoli sugli argomenti di una funzione possono essere combinati: In[177]:= fattoriale4@n_Integer ê; n > 0D := n ! Mathematica mette a disposizione una serie di funzioni booleane (i.e. funzioni che assumono solo i valori True o False) che possono essere utilizzate per verificare svariate condizioni: Less Greater LessEqual GreaterEqual NumberQ NumericQ IntegerQ EvenQ OddQ PrimeQ Positive Negative TrueQ FreeQ AtomQ MatchQ ValueQ Ñ Ñ Ñ Ñ Ñ Ñ Ñ Ñ Ñ Ñ Ñ Ñ Ñ Ñ Ñ Ñ Ñ Qualora per una funzione siano fatte più definizioni di tipo condizionale, nel momento in cui viene specificato un argomento, le condizioni sono considerate in ordine cronologico di definizione fino a quando non se ne trova una che risulta verificata dall'argomento, e viene utilizzata la definizione corrispondente: In[197]:= f@x_D := 1 ê; x > 0; f@x_D := 2 ê; EvenQ@xD; In[202]:= f@3D Out[202]= 1 cap0.nb In[203]:= Out[203]= In[204]:= Out[204]= 31 f@2D 1 f@-2D 2 In[205]:= f@-3D Out[205]= f@-3D 1.13.4. Funzioni con un numero variabile di argomenti E' possibile specificare il comportamento di funzioni che hanno un numero variabile di argomenti, indicando con un doppio carattere di underscore una sequenza di uno o più argomenti e con un triplo carattere di underscore una sequenza di zero o più argomenti: ad esempio la definizione In[246]:= g@x___, a_, y___, a_, z___D := a g@a___D := 0 implica che la funzione g, calcolata su un qualunque numero di argomenti, ritorna 0 a meno che due argomenti abbiano lo stesso valore, che in questo caso coincide con il valore ritornato dalla funzione: In[250]:= Out[250]= In[251]:= Out[251]= g@1, 2, 3, 4, 5, 6D 0 g@1, 2, 3, 4, 6, 6D 6 32 cap0.nb 1.13.5. Pattern In Mathematica la definizione degli argomenti di una funzione rappresenta il caso particolare della definizione di un pattern, cioé del denotatore di un insieme di espressioni. Il carattere di underscore viene utilizzato per indicare una qualunque espressione, a cui è possibile assegare un nume di riferimento, da far precedere al carattere stesso. Ciò permette di riutilizzare lo stesso pattern una qualunque espressione la somma di due espressioni _ _+_ x ^ n_ un' espressione elevata a qualunque potenza un' espressione elevata a qualunque potenza il simbolo x elevato a qualunque potenza f@n_, n_D la funzione f calcolata su due argomenti identici _^_ x_ ^ n_ 1.13.6. Funzioni pure Consideriamo la seguente definizione di una semplice funzione: In[39]:= f@x_D := x ê 2 + 1 La parte essenziale della definizione consiste nel descrivere come l'argomento viene modificato, e ciò è indipendente dal nome che si è scelto per la funzione (avremmo potuto chiamarla g, h o dimezzaEAggiungiUno, e il suo comportamento non sarebbe variato) e ancora di più dal nome che si è deciso di dare al suo argomento ( x sta infatti ad indicare un valore formale, che verrà di volta in volta rimpiazzato dal valore effettivamente inserito come parametro). Mathematica permette di definire una funzione specificando solamente questo tipo di informazione essenziale: ad esempio la precedente definizione della funzione f è equivalente a In[42]:= #ê2+ 1 & in cui viene definita una funzione pura, in cui cioé non sono specificati nomi per la funzione stessa o per il suo argomento: questo viene indicato dal carattere di cancelletto ( #), e la definizione è terminata dal carattere di ampersand (&). Una funzione pura, una volta definita, può essere usata all'interno di un'espressione: ad esempio f = #ê2+ 1 & In questo caso l'assegnamento riguarda l'intera funzione, e quindi permette di salvare la funzione stessa per poterla riutilizzare in futuro. Oppure è possibile valutare la funzione cap0.nb In[44]:= Out[44]= 33 # ê 2 + 1 &@1D 3 ÅÅÅÅ 2 In realtà le funzioni pure vengono tipicamente utilizzate come funzioni "usa e getta" da specificare come argomenti di altre funzioni (cfr. ad esempio con la descrizione di Select o di Sort). Nel caso si volesse definire una funzione pura con più di un argomento sarà sufficiente indicare il primo argomento con #1, il secondo con #2, il terzo con #3 e così via: In[43]:= Out[43]= #1 ê #2 &@3, 7D 3 ÅÅÅÅ 7 Attenzione! E' sempre necessario terminare la definizione di una funzione pura utlizzando il carattere di ampersand, che serve appunto a Mathematica per capire cosa all'interno di un'espressione deve essere interpretato come la descrizione di una funzione e cosa no. Molti degli errori nel codice che utilizza funzioni pure sono legati all'omissione del carattere di ampersand. 1.14. Opzioni Nelle precedenti sezioni abbiamo visto come utilizzare degli argomenti opzionali specificando dei valori predefiniti. La soluzione adottata manteneva il carattere di posizionalità degli argomenti: ad esempio, nella seguente funzione con quattro argomenti di cui gli ultimi due sono opzionali In[253]:= f@arg1_, arg2_, arg3_: 1, arg4_: 2D := 8arg1, arg2, arg3, arg4< non è possibile specificare un valore esplicito per il quarto argomento utilizzando nel contempo il valore predefinito per il terzo. Un'altra possibilità di definire argomenti opzionali prescindendo dalla posizionalità degli stessi consiste nel fare riferimento ad opzioni aventi un nome prefissato. Tale nome deve essere specificato per modificare l'opzione corrispondente, in modo da permettere di modificare un qualunque sottoinsieme delle opzioni possibili, indipendentemente dall'ordine in cui vengono indicate. Per modificare un'opzione è necessario indicare il relativo nome, seguito dal carattere di freccia ( Ø) o in alternativa dalla sequenza -> e infine dal valore a cui settare l'opzione. Consideriamo ad esempio la funzione PrimeQ, che ritorna True se l'argomento passato è un numero primo e False altrimenti. In[266]:= Out[266]= PrimeQ@5D True 34 cap0.nb In[276]:= Out[276]= PrimeQ@6D False Per visualizzare tutte le opzioni di una funzione, unitamente ai corrispondenti valori predefiniti, è possibile utilizzare la funzione Options: In[277]:= Out[277]= Options@PrimeQD 8GaussianIntegers Ø False< L'opzione GaussianIntegers permette di verificare la primalità nel modo usuale quando è settata a False e rispetto alla scomposizione in fattori complessi aventi parti reale e immaginaria intere quando settata a True. In[267]:= Out[267]= PrimeQ@5, GaussianIntegers Ø TrueD False 1.15. Dare un nome alle cose Come abbiamo visto nelle sezioni precedenti, Mathematica può essere esteso definendo dei nuovi oggetti, che assumono la forma di variabili e di funzioni definite dall'utente. In entrambi i casi è necessario specificare per questi oggetti un nome univoco, che va scelto rispettando l'unica regola formale di non andare a utilizzare un nome già esistente. L'esito di questa operazione porterebbe infatti a cancellare l'associazione tra il nome e il significato già esistente in favore di quello nuovo. In realtà Mathematica implementa un meccanismo di protezione per i nomi che ne impedisce la ridefinizione: se infatti proviamo ad esempio a ridefinire la funzione Tan otteniamo il seguente errore In[125]:= Tan@x_D := Sin@xD ê Cos@xD; SetDelayed::write : Tag Tan in Tan@x_D is Protected. More… che ci avvisa di come il nome Tan sia protetto, e cioé non ridefinibile. Attenzione! Il meccanismo di protezione è applicato automaticamente a tutti i comandi definiti da Mathematica ma è necessario invocarlo esplicitamente per applicarlo a nomi di oggetti definiti dall'utente. Per maggiori informazioni fare riferimento alle funzioni Protect e Unprotect. Nella pratica si seguono però altre convenzioni nell'attribuzione dei nomi agli oggetti definiti dall'utente che, nonostante la loro violazione non venga intercettata e segnalata da Mathematica come un errore, contribuiscono a migliorare la leggibilià del codice prodotto. In particolare: E' opportuno assegnare a una variabile o a una funzione l'esatto nome che avrebbe nel linguaggio matematico. Questa convenzione, utilizzata per tutte le funzioni implementate in Mathematica, ha il duplice vantaggio di: i) evitare di generare codice ambiguo da interpretare (in alcuni sistemi Int sta a rappresentare la parte intera di un numero, in altri l'operatore di integrazione; Mathematica usa rispettivamente IntegerPart e Integrate), e ii) permettere a un utente che non conosce il nome esatto di una funzione di reperirlo facilmente conoscendo l'operazione cap0.nb 35 E' opportuno assegnare a una variabile o a una funzione l'esatto nome che avrebbe nel linguaggio matematico. Questa convenzione, utilizzata per tutte le funzioni implementate in Mathematica, ha il duplice vantaggio di: i) evitare di generare codice ambiguo da interpretare (in alcuni sistemi Int sta a rappresentare la parte intera di un numero, in altri l'operatore di integrazione; Mathematica usa rispettivamente IntegerPart e Integrate), e ii) permettere a un utente che non conosce il nome esatto di una funzione di reperirlo facilmente conoscendo l'operazione matematiche che essa implementa. Mathematica è case sensitive, nel senso che riconosce come differenti le versioni in minuscolo e in maiuscolo di un carattere. In altre parole la parola log è riconosciuta e interpretata in modo diverso dalla parola Log. Per convenzione, tutti i nomi definiti in Mathematica sono composti accostando una serie di parole scritte in caratteri minuscoli, a eccezione delle inziali di ogni singola parola (sono nomi definiti in Mathematica ad esempio Pi, E, I, Sqrt, InputForm e FractionalPart). Per rimarcare la differenza tra gli oggetti predefiniti in Mathematica e quelli aggiunti dall'utente, i nomi di questi ultimi seguono le stesse regole, con l'eccezione che iniziano sempre con una lettera minuscola (come ad esempio la funzione multiploPi che abbiamo definito nella sezione precedente). L'utilizzo di queste regole ha lo svantaggio di generare spesso nomi composti da un elevato numero di caratteri, che quindi risultano più complessi da immettere tramite la tastiera. Questo inconveniente può essere evitato usando la funzione di completamento automatico dell'input: scrivendo parzialmente un comando di Mathematica o il nome di un oggetto definito dall'utente e selezionando la voce di menù Input/Complete Selection (o le combinazioni di tasti Ì-K su Macintosh, ‚-K su Windows e ‡-K su Linux), il nome viene completato automaticamente qualora vi sia un solo oggetto che inizia esattamente nel modo scritto; qualora esistano più alternative, queste vengono visualizzate in una lista da cui è possibile scegliere il comando da scrivere. Se infine i caratteri immessi non corrispondono ad alcun oggetto correntemente definito in Mathematica, il sistema tenta di interpretare l'input immesso e visualizza un elenco di comandi simili a quanto inserito. Qualora non si conosca esattamente quale sia il nome di una funzione o di una costante predefinite in Mathematica, o quando non si ricordino esattamente quanti e quali argomenti ha una funzione, è possibile utilizzare il sistema di help on-line per reperire le informazioni mancanti. Utilizzando la voce di menù Help/Help browser è possibile visualizzare una finestra in cui: ricercare informazioni relativamente a un oggetto di cui si conosce il nome, scrivendolo in un'apposita area di testo: man mano che il testo viene scritto, automaticamente viene visualizzata, all'interno di un elenco strutturato, la scelta più simile a quanto digitato. La selezione di una voce in questo elenco permette di ottenere una descrizione dell'oggetto corrispondente, dei rimandi sia a comandi simili che alla documentazione cartacea e una serie di esempi di utilizzo; percorrere l'elenco strutturato alla ricerca di informazioni di un oggetto di cui non si conosce il nome esatto. La struttura presente permette di consultare, tra le altre cose: degli elenchi alfabetici e gerarchici degli oggetti implementati che la digitalizzazione della documentazione cartacea. 36 cap0.nb Tale strumento di consultazione è anche disponibile su internet collegandosi tramite web browser al sito http://documents.wolfram.com. E' infine possibile selezionare una serie di caratteri in un notebook e consultare direttamente la documentazione utilizzando la voce di menù Help/Find Selected Function (o le combinazioni di tasti Ì-˜-F su Macintosh, ‚-˜-F su Windows e ‡-˜-F su Linux). 1.16. Stringhe Una stringa è un insieme di caratteri. In Mathematica le stringhe vengono indicate racchiudendole tra doppi apici: In[11]:= Out[11]= "questa è una stringa" questa è una stringa Qualora fosse necessario inserire in una stringa il carattere di doppio apice, è sufficiente farlo precedere dal carattere di backslash (\): In[14]:= Out[14]= "questa è una stringa contenente il carattere doppio apice H\"L" questa è una stringa contenente il carattere doppio apice H"L Qualora fosse invece necessario inserire in una stringa il carattere di backslash, è sufficiente farlo precedere da un altro carattere di backslash (\): In[13]:= Out[13]= "questa è una stringa contenente il carattere di backslash H\\L" questa è una stringa contenente il carattere di backslash H\L E' essenziale inserire le stringhe entro doppi apici. In caso contrario il contenuto del messaggio verrà intrepretato in modo differente. Se ad esempio la stringa precedente venisse inserita senza utilizzare i doppi apici, otterremmo In[145]:= questa è una stringa Out[145]= è questa stringa una in cui le singole parole sono state interpretate come simboli particolari (cfr. la sezione seguente sulle variabili) che sono poi stati visualizzati in ordine alfabetico. Mathematica implementa, tra le altre, le seguenti funzioni che agiscono su stringhe: StringLength ritorna la lunghezza di una stringa, cioé il numero di caratteri in essa contenuti cap0.nb 37 In[280]:= Out[280]= In[281]:= Out[281]= StringLength@"Stringa"D 7 StringLength@""D 0 StringJoin ritorna la stringa ottenuta giustapponendo le stringhe passategli come argomenti (in un numero variabile): In[283]:= Out[283]= StringJoin@"Questa ", "è ", "una", " stringa"D Questa è una stringa E' possibile utilizzare la sequenza <> come abbreviazione per StringJoin: In[284]:= Out[284]= "Una " <> "stringa" Una stringa StringTake estrae una parte della stringa passata come primo argomento; il secondo argomento determina quale parte estrarre Formato StringTake@"stringa", StringTake@"stringa", StringTake@"stringa", StringTake@"stringa", In[290]:= Out[290]= In[291]:= Out[291]= Esito nD Estrae i primi n caratteri -nD Estrae gli ultimi n caratteri 8n<D Estrae il carattere in posizione n 8n, m<D Estrae i caratteri nelle posizioni dalla n alla m StringTake@"Una stringa", 3D Una StringTake@"Una stringa", -3D nga 38 cap0.nb In[293]:= Out[293]= StringTake@"Una stringa", 83, 6<D a st StringDrop si comporta in modo duale rispetto a StringTake, di cui ha il medesimo formato: i suoi due argomenti rappresentano rispettivamente una stringa e una serie di caratteri da eliminare dalla stringa stessa per determinare il valore da ritornare: In[294]:= Out[294]= In[295]:= Out[295]= StringDrop@"Una stringa", 3D stringa StringDrop@"Una stringa", 83, 6<D Unringa StringReplace effettua delle sostituzioni all'interno di una stringa, passata come primo argomento, sulla base di una o più regole passate come secondo argomento. Una regola ha la forma stringaoriginale Ø stringamodificata. Nel caso si vogliano specificare più regole è sufficiente separarle tramite virgole e racchiudere l'intero gruppo in una coppia di parentesi graffe (vedremo in seguito come questa operazione, a cui ci riferiremo come alla creazione di una lista, sia altamente usata in tutti gli aspetti di Mathematica). In[296]:= Out[296]= In[297]:= Out[297]= StringReplace@"Una stringa", "st" Ø "a"D Una aringa StringReplace@"abcde", 8"a" Ø "b", "b" Ø "c", "c" Ø "d", "d" Ø "e", "e" Ø "f"<D bcdef ToUpperCase e ToLowerCase ritornano la versione, rispettivamente formate da soli caratteri maiuscoli e minuscoli, della stringa passata come argomento: In[10]:= ToUpperCase@"Stringa"D ToLowerCase@"Stringa"D Out[10]= STRINGA Out[11]= stringa cap0.nb 39 ToString accetta come argomento un'espressione di cui determina il valore che provvede poi a trasformare in una stringa: In[17]:= Out[17]= ToString@N@PiDD <> "b" 3.14159b 1.17. Liste Una lista rappresenta un insieme di valori. Per fare riferimento a una lista in Mathematica è necessario indicare i relativi valori separandoli tramite dei caratteri di virgola (,) e delimitando il tutto tramite una coppia di parentesi graffe: In[17]:= Out[17]= 86, 5, 4, 3, 2, 1< 86, 5, 4, 3, 2, 1< I valori contenuti in una lista possono avere natura eterogenea In[18]:= Out[18]= 81.4, Pi, "ciao"< 81.4, p, ciao< Per accedere l'elemento di posto i una lista è sufficiente posporre alla lista stessa il numero i entro una coppia di doppie parentesi quadre: In[19]:= Out[19]= 86, 5, 4, 3, 2, 1<@@4DD 3 Sono posizioni valide tutti i numeri interi che vanno da 1 al numero di elementi contenuti nella lista. Qualora si specifichi come posizione un numero maggiore del numero di elementi nella lista, o un numero non intero, Mathematica emetterà un errore: In[24]:= 86, 5, 4, 3, 2, 1<@@10DD Part::partw : Part 10 of 86, 5, 4, 3, 2, 1< does not exist. More… Out[24]= 86, 5, 4, 3, 2, 1<P10T 40 cap0.nb In[21]:= 86, 5, 4, 3, 2, 1<@@4.2DD Part::pspec : Part specification 4.2` is neither an integer nor a list of integers. More… Out[21]= 86, 5, 4, 3, 2, 1<P4.2T E' possibile specificare come posizione un numero negativo: in questo caso il conteggio delle posizioni avverrà partendo dall'ultimo elemento e procedendo a ritroso: -1 indica l'ultimo elemento della lista, -2 il penultimo e così via: In[25]:= Out[25]= 86, 5, 4, 3, 2, 1<@@-3DD 3 E' infine possibile specificare 0 come posizione all'interno di una lista. Il risultato sarà List indipendentemente dalla lista utilizzata: In[26]:= Out[26]= In[27]:= Out[27]= 86, 5, 4, 3, 2, 1<@@0DD List 81.4, Pi, "ciao"<@@0DD List Il motivo di questo comportamento risiede nel modo in cui Mathematica memorizza l'input che gli viene passato (cfr sezioni successive). Fino ad ora abbiamo costruito liste in modo elementi che vi appertenevano. Molto spesso modo intensivo, indicando una proprietà che stessa. Mathematica permette di definire liste funzione Table, la cui sintassi è estensivo, elencando cioé in modo esplicito tutti gli è possibile (e anche più succinto) definire una lista in viene soddisfatta da tutti e soli gli elementi della lista utilizzando una variante di questa modalità, tramite la Table@ < espressione >, < iteratore >D dove <iteratore> è una lista di tre elementi di cui il primo rappresenta il nome di una variabile e i rimanenti sono espressioni valide; quindi {i,1,10}, {e,2^x,a} e {q,-a,E^4} sono iteratori validi, mentre ad esempio {1,2,3} non lo è; cap0.nb 41 <espressione> è un'espressione valida che tipicamente dipende dalla variabile che compare nell'iteratore. Gli elementi della lista costruita dalla funzione Table si ottengono nel seguente modo: la variabile specificata nell'iteratore viene inizializzata al valore di partenza specificato in seconda posizione nell'iteratore stesso; se il valore della variabile non è superiore al valore finale specificato in terza posizione nell'iteratore, viene valutata l'espressione passata come primo argomento a Table, e il risultato viene aggiunto alla lista; il valore della variabile viene incrementato di un'unità e il punto precedente viene nuovamente eseguito. Ad esempio In[139]:= Out[139]= In[140]:= Out[140]= In[141]:= Out[141]= Table@i, 8i, 1, 5<D 81, 2, 3, 4, 5< Table@2 i, 8i, 1, 5<D 82, 4, 6, 8, 10< Table@2 ^ i, 8i, 1, 5<D 82, 4, 8, 16, 32< 1.17.1. Forme alternative per gli iteratori E' possibile fare riferimento alle seguenti forme alternative per definire un iteratore: Nella forma a quattro elementi, è possibile indicare di quanto incrementare di volta in volta la variabile. In altre parole la forma {variabile, inizio, fine} inizialmente introdotta equivale a {variabile, inizio, fine, 1}. Ciò permette ad esempio di inserire i numeri pari in una lista in modo più intuitivo In[142]:= Out[142]= Table@i, 8i, 2, 10, 2<D 82, 4, 6, 8, 10< o di effettuare conteggi al contrario 42 cap0.nb In[143]:= Out[143]= Table@i ê 2, 8i, 10, 1, -1<D 9 7 5 3 1 95, ÅÅÅÅ , 4, ÅÅÅÅ , 3, ÅÅÅÅ , 2, ÅÅÅÅ , 1, ÅÅÅÅ = 2 2 2 2 2 Nella forma a due elementi vengono specificati solamente il nome della variabile e il valore finale. Il valore iniziale e l'incremento vengono implicitamente settati al valore predefinito 1. Pertanto l'iteratore {variabile, fine} equivale a {variabile, 1, fine}, o equivalentemente a {variabile, 1, fine, 1}. In[9]:= Out[9]= Table@i, 8i, 10<D 81, 2, 3, 4, 5, 6, 7, 8, 9, 10< Nella forma a un elemento si specifica esclusivamente il valore finale. Anche in questo caso il valore iniziale e l'incremento sono settati a 1, ma non viene esplicitamente fatto riferimento ad alcuna variabile. Tale tipologia di iteratore è utile nella definizione di liste costanti: In[10]:= Out[10]= Table@1, 810<D 81, 1, 1, 1, 1, 1, 1, 1, 1, 1< 1.17.2. Funzioni per le liste Length ritorna il numero di elementi della lista passatagli come argomento: In[26]:= Out[26]= Length@85, 4, 3, 2, 1<D 5 Part è l'equivalente delle doppie parentesi quadre: ritorna parte del contenuto di una lista che gli viene passata come primo argomento. Quando il secondo argomento è un numero, viene ritornato l'elemento nella posizione corrispondente; quando è una lista, vengono ritornati gli elementi nelle posizioni specificatevi, a loro volta racchiuse in una lista: In[18]:= Out[18]= Part@85, 4, 3, 2, 1<, 2D 4 cap0.nb 43 In[21]:= Out[21]= Part@85, 4, 3, 2, 1<, 82, 4<D 84, 2< come per le doppie parentesi quadre, se una posizione negativa indica che il conteggio va fatto a ritroso a partire dall'ultimo elemento della lista. First e Last ritornano rispettivamente il primo e l'ultimo elemento di una lista In[19]:= First@85, 4, 3, 2, 1<D Last@85, 4, 3, 2, 1<D Out[19]= 5 Out[20]= 1 Rest ritorna la lista passata come argomento dopo avervi rimosso il primo elemento: In[28]:= Out[28]= Rest@85, 4, 3, 2, 1<D 84, 3, 2, 1< Take ritorna i valori di una lista (passata come primo argomento) che occupano posizioni successive: in particolare, se il secondo argomento è un intero n, verranno ritornati i primi o gli ultimi n elementi, a seconda che n sia positivo o negativo: In[22]:= Out[22]= Take@85, 4, 3, 2, 1<, 2D 85, 4< se invece il secondo argomento è {n, m}, con n minore o uguale a m, verranno ritornati tutti gli elementi dalla m-esima alla n-esima posizione (estremi inclusi) In[31]:= Out[31]= Take@85, 4, 3, 2, 1<, 82, 4<D 84, 3, 2< Drop elimina da una lista (passata come primo argomento) una o più posizioni (specificate dal secondo argomento, secondo la stessa codifica usata per Part) e ritorna la lista risultante 44 cap0.nb In[24]:= Out[24]= In[25]:= Out[25]= Drop@85, 4, 3, 2, 1<, 2D 83, 2, 1< Drop@85, 4, 3, 2, 1<, 82, 4<D 85, 1< Select determina quali elementi di una lista (passata come primo argomento) verificano un criterio, specificato come secondo argomento nella forma di una funzione che ritorna True o False. Tale funzione può essere una delle funzioni predefinite di Mathematica In[45]:= Out[45]= Select@81, 2, 3, 4, 5<, EvenQD 82, 4< In realtà molto spesso è necessario ricorrere dei criteri più sofisticati, che possono essere introdotti esternamente tramite la definizione di una funzione apposita In[48]:= Out[49]= criterio@x_D := x > 2 && x § 4; Select@81, 2, 3, 4, 5<, criterioD 83, 4< in alternativa è possibile introdurre i criteri direttamente all'interno di Select, tramite la definizione di una funzione pura In[47]:= Out[47]= Select@81, 2, 3, 4, 5<, # > 2 && # § 4 &D 83, 4< Cases funziona in modo analogo a Select, con la differenza che il criterio viene rappresentato tramite un pattern piuttosto che tramite una funzione booleana: In[7]:= Out[7]= Cases@82 ^ 2, E ^ 4, 1 ê x, a, x ^ Pi, Log@xD, 1 + x ^ 7<, _ ^ _D 1 9‰4 , ÅÅÅÅ , xp = x cap0.nb 45 In[8]:= Out[8]= Cases@82 ^ 2, E ^ 4, 1 ê x, a, x ^ Pi, Log@xD, 1 + x ^ 7<, x ^ _D 1 9 ÅÅÅÅ , xp = x Attenzione! Il simbolo 2^2 non fa parte dell'output di Cases, in quanto viene semplificato in 4 prima dell'esecuzione. Il comportamento di Cases è simile a quello di altre due funzioni: Count e Position, che ritornano rispettivamente il numero e la posizione degli elementi di una lista che combaciano con un dato pattern: In[9]:= Count@82 ^ 2, E ^ 4, 1 ê x, a, x ^ Pi, Log@xD, 1 + x ^ 7<, _ ^ _D Out[9]= 3 In[10]:= Position@82 ^ 2, E ^ 4, 1 ê x, a, x ^ Pi, Log@xD, 1 + x ^ 7<, _ ^ _D Out[10]= 882<, 83<, 85<, 87, 2<< Prepend e Append inseriscono un elemento in testa e in coda ad una lista, rispettivamente In[13]:= Out[13]= In[14]:= Out[14]= Prepend@81, 2, 3<, 0D 80, 1, 2, 3< Append@81, 2, 3<, 4D 81, 2, 3, 4< Insert aggiunge un elemento in una lista, specificando la posizione che questo andrà ad assumere. Delete rimuove da una lista l'elemento in una data posizione. ReplacePart modifica l'elemento di una lista in una specifica posizione: In[15]:= Out[15]= Insert@81, 2, 3, 4<, 2.5, 3D 81, 2, 2.5, 3, 4< 46 cap0.nb In[16]:= Out[16]= In[17]:= Out[17]= Delete@81, 2, 3, 4<, 3D 81, 2, 4< ReplacePart@81, 2, 3, 4<, 2.5, 3D 81, 2, 2.5, 4< Join unisce due o più liste: In[20]:= Out[20]= Join@8a, b, c<, 8d, e, f<D 8a, b, c, d, e, f< Union, Intersection e Complement implementano rispettivamente l'unione, l'intersezione e il complemento di liste interpretate come se fossero degli insiemi In[23]:= Out[23]= In[21]:= Out[21]= In[22]:= Out[22]= Union@8a, b, c, d<, 8c, d, e, f<D 8a, b, c, d, e, f< Intersection@8a, b, c, d<, 8c, d, e, f<D 8c, d< Complement@81, 2, 3, 4, 5<, 82, 4<D 81, 3, 5< Sort ordina gli elementi di una lista. Nella sua versione standard, i dati vengono arrangiati in ordine non decrescente In[24]:= Out[24]= Sort@81, 5, 7, 4, 3, 6<D 81, 3, 4, 5, 6, 7< è possibile indicare un secondo argomento che specifica un criterio su cui basare l'ordinamento, consistente una funzione di due argomenti che ritorna True se il primo argomento deve comparire prima del secondo. cap0.nb In[26]:= Out[26]= 47 Sort@81, 5, 7, 4, 3, 6<, #2 < #1 &D 87, 6, 5, 4, 3, 1< 1.18. Visualizzazione di liste Consideriamo la seguente lista In[456]:= lista = 81, 5, 3, 9, 4, 8<; e analizziamo i possibili modi di elencarne gli elementi. In generale il contenuto di una lista può essere visualizzato: in forma estensiva, elencando i suoi elementi tramite l'usuale notazione In[457]:= Out[457]= lista 81, 5, 3, 9, 4, 8< in forma tabulare, scrivendo ogni elemento su una riga differente tramite la funzione TableForm In[270]:= lista êê TableForm Out[270]//TableForm= 1 5 3 9 4 8 in forma grafica, usando la funzione ListPlot per rappresentare ogni elemento tramite un punto che ha come ordinata il valore dell'elemento stesso e come ascissa la sua posizione all'interno della lista 48 cap0.nb ListPlot@listaD In[273]:= 8 6 4 2 Out[273]= 3 4 5 6 Ü Graphics Ü Attenzione! La funzione ListPlot, oltre a produrre e visualizzare un grafico, ritorna il grafico stesso come oggetto (che in realtà corrisponde a un tipo particolare di lista). E' per questo che in corrispondenza della cella di input è stata prodotta una cella di output il cui contenuto è - Graphics -. L'uso del carattere di punto e virgola permette di eliminare quest'ultima visualizzazione. La funzione ListPlot è corredata da un considerevole numero di opzioni: In[275]:= Out[275]= Options@ListPlotD 1 9AspectRatio Ø ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ , Axes Ø Automatic, AxesLabel Ø None, GoldenRatio AxesOrigin Ø Automatic, AxesStyle Ø Automatic, Background Ø Automatic, ColorOutput Ø Automatic, DefaultColor Ø Automatic, DefaultFont ß $DefaultFont, DisplayFunction ß $DisplayFunction, Epilog Ø 8<, FormatType ß $FormatType, Frame Ø False, FrameLabel Ø None, FrameStyle Ø Automatic, FrameTicks Ø Automatic, GridLines Ø None, ImageSize Ø Automatic, PlotJoined Ø False, PlotLabel Ø None, PlotRange Ø Automatic, PlotRegion Ø Automatic, PlotStyle Ø Automatic, Prolog Ø 8<, RotateLabel Ø True, TextStyle ß $TextStyle, Ticks Ø Automatic= tra queste, l'unica opzione che si riferisce espressamente a ListPlot è PlotJoined, settabile a True o False, che permette di decidere se il grafico deve essere fatto congiungendo i punti tra di loro: cap0.nb 49 ListPlot@lista, PlotJoined Ø TrueD; In[272]:= 8 6 4 2 3 4 5 6 tutte le altre opzioni agiscono sul modo in cui viene prodotto l'oggetto grafico. 1.19. Liste innestate Una lista può contenere elementi di natura eterogenea: ad esempio la lista In[31]:= Out[31]= 8Pi, N@PiD, "Pi"< 8p, 3.14159, Pi< contiene il valore esatto di p, il suo valore approssimato e la stringa "Pi" rispettivamente in prima, seconda e terza posizione. Più in generale è possibile costruire liste che annoverino tra i loro elementi altre liste In[34]:= listaInnestata = 8Pi, N@PiD, "Pi", 81, 2, 3<<; tali liste vengono dette di tipo innestato. I loro elementi vengono acceduti nel modo usuale, tenuto conto del fatto che ora un elemento può essere una lista In[37]:= listaInnestata@@1DD listaInnestata@@4DD@@1DD Length@listaInnestata@@4DDD Out[37]= p Out[38]= 1 Out[39]= 3 50 cap0.nb Qualora l'argomento di ListPlot sia una lista composta esclusivamente da liste di due elementi, ognuna di queste liste viene interpretata come la descrizione di un punto nel piano (con l'usuale codifica in cui il primo elemento corrisponde all'ascissa e il secondo all'ordinata). In[40]:= listaPunti = 883, 1<, 87, 5<, 88, 3<, 89, 9<, 81, 4<, 82, 8<<; ListPlot@listaPunti, PlotJoined Ø TrueD; 8 6 4 4 6 8 Il contenuto di una lista innestata è visualizzabile anche in forma tabulare utilizzando la funzione TableForm: In[42]:= listaPunti êê TableForm Out[42]//TableForm= 3 7 8 9 1 2 1 5 3 9 4 8 Questa funzione ha il vantaggio di poter essere utilizzata anche con liste aventi una struttura più complessa di quelle utilizzabili con ListPlot: In[43]:= listaComplicata = 881, 2, 3, 4<, 8a, b, c, d<, 8A, B, C, D<, 8i, ii, iii, iv<<; listaComplicata êê TableForm Out[44]//TableForm= 1 a A i 2 b B ii 3 c C iii 4 d D iv cap0.nb 51 in particolare, la visualizzazione prodotta da TableForm è particolarmente significativa con liste a una o due dimensioni o livelli. In realtà Mathematica permette di gestire anche liste con più livelli. In alcuni casi si rende necessario trasformare una lista innestata in una lista semplice, o più in generale di ridurre il numero di livelli di una lista, "fondendo" assieme i livelli superiori. Questa operazione viene realizzata in Mathematica dalla funzione Flatten, che riduce qualunque lista passatagli a una lista a un solo livello: In[60]:= Out[60]= In[64]:= Out[64]= Flatten@881, 2, 3<, 8a, b, c<<D 81, 2, 3, a, b, c< Flatten@888a, b<, 8A, B<<, 88x, y<, 8X, Y<<<D 8a, b, A, B, x, y, X, Y< Nel caso si voglia fondere solo un determinato numero di livelli, è sufficiente specificare tale numero come secondo argomento In[65]:= Out[65]= Flatten@888a, b<, 8A, B<<, 88x, y<, 8X, Y<<<, 1D 88a, b<, 8A, B<, 8x, y<, 8X, Y<< 1.20. Matrici e vettori Le liste vengono frequentemente utilizzate per codificarvi dei vettori. In[47]:= vettore1 = 81, 0, 1<; vettore2 = 81, 1, 0<; Mathematica predispone una serie di funzioni che agiscono su liste interpretandone il contenuto come se fossero dei vettori: Dot e Cross implementano rispettivamente i prodotti scalare e vettore. Dot può essere utilizzando il simbolo di punto (.) in modalità infissa (cioé tra i due operatori) In[50]:= Out[50]= vettore1.vettore2 1 52 cap0.nb In[51]:= Out[51]= Cross@vettore1, vettore2D 8-1, 1, 1< Norm calcola la norma Euclidea di un vettore. Se viene specificato come secondo argomento un numero p, viene calcolata la p-norma (dove p può essere infinito) In[52]:= Out[52]= In[58]:= Out[58]= In[59]:= Out[59]= Norm@vettore1D è!!! 2 Norm@vettore1, 5D 21ê5 Norm@vettore1, ¶D 1 Analogamente, liste a due dimensioni possono essere utilizzate per memorizzare delle matrici. Mathematica mette a disposizione la funzione MatrixForm per visualizzare una lista formattandola nel modo usato tipicamente per indicare le matrici: In[66]:= matr = 881, 0, 1<, 81, 1, 0<, 80, 0, 1<<; matr êê MatrixForm Out[67]//MatrixForm= 1 0 1y i j z j z j j 1 1 0z z j z j z k0 0 1{ Come per il caso dei vettori, è possibile utilizzare una serie di funzioni specializzate nella manipolazione di matrici: Gli operatori di somma e sottrazione sono sovraccaricati in modo da funzionare correttamente con le matrici cap0.nb In[68]:= 53 matr + matr êê MatrixForm Out[68]//MatrixForm= 2 0 2y i j z j z j j 2 2 0z z j z j z k0 0 2{ analogamente, l'operatore usato per il prodotto scalare tra vettori può essere utilizzato anche per moltiplicare tra di loro due matrici In[70]:= matr.matr êê MatrixForm Out[70]//MatrixForm= 1 0 2y i j z j z j j 2 1 1z z j z j z k0 0 1{ e' possibile generare automaticamente le matrici identità di una generica dimensione tramite la funzione IdentityMatrix In[79]:= IdentityMatrix@3D êê MatrixForm Out[79]//MatrixForm= 1 0 0y i j z j z j j 0 1 0z z j z j z k0 0 1{ Transpose calcola la trasposta di una matrice In[72]:= Transpose@matrD êê MatrixForm Out[72]//MatrixForm= 1 1 0y i j z j z j z j j0 1 0z z j z k1 0 1{ Inverse calcola l'inversa di una matrice In[75]:= matrInv = Inverse@matrD; matrInv êê MatrixForm Out[76]//MatrixForm= i 1 0 -1 y j z j z j j -1 1 1 z z j z j z k 0 0 1 { 54 cap0.nb In[77]:= matr.matrInv êê MatrixForm Out[77]//MatrixForm= 1 0 0y i j z j z j j 0 1 0z z j z j z k0 0 1{ Det calcola il determinante di una matrice In[80]:= Out[80]= Det@matrD 1 Eigenvalues ritorna una lista contenente gli autovalori della matrice passatagli come argomento. Analogamente Eigenvectors calcola gli autovettori, indicandoli come una lista di vettori (che in ultima analisi è una lista di liste di due elementi) In[85]:= Out[86]= In[87]:= Out[87]= matr1 = 881, 3<, 83, 3<<; Eigenvalues@matr1D 92 + è!!!!!! è!!!!!! 10 , 2 - 10 = Eigenvectors@matr1D 1 1 è!!!!!! è!!!!!! 99-1 + ÅÅÅÅ I2 + 10 M, 1=, 9-1 + ÅÅÅÅ I2 - 10 M, 1== 3 3 1.20.1. Una digressione: opzioni per gli oggetti di tipo Graphics Abbiamo visto come utilizzando ListPlot sia possibile produrre grafici contenenti punti o linee spezzate. In realtà è possibile utilizzare Mathematica per produrre grafici utilizzando strumenti più complessi. Quali che siano questi strumenti, è possibile specificare delle direttive che modificano il modo in cui il grafico viene visualizzato. Ad esempio PlotRange, che indica quale intervallo degli assi cartesiani debba essere visualizzato. E' possibile specificare come valore per questa opzione una lista a uno o due livelli. Nel primo caso la lista contiene due valori che indicano quale intervallo visualizzare per l'asse delle ordinate, nel secondo la lista contiene due liste che indicano quale intervallo visualizzare rispettivamente per l'asse delle ascisse e delle ordinate. E' possibile utilizzare al posto di una di queste liste il simbolo Automatic: in questo caso Mathematica determinerà automaticamente quale intervallo visualizzare. cap0.nb In[129]:= 55 listaPunti = 883, 1<, 87, 5<, 88, 3<, 89, 9<, 81, 4<, 82, 8<<; ListPlot@listaPunti, PlotJoined Ø TrueD; 8 6 4 4 In[131]:= 6 8 ListPlot@listaPunti, PlotJoined Ø True, PlotRange Ø 80, 10<D; 10 8 6 4 2 4 6 8 56 cap0.nb ListPlot@listaPunti, PlotJoined Ø True, PlotRange Ø 880, 10<, Automatic<D; In[132]:= 8 6 4 2 In[133]:= 4 6 8 10 ListPlot@listaPunti, PlotJoined Ø True, PlotRange Ø 880, 10<, 80, 8<<D; 8 7 6 5 4 3 2 1 2 4 6 8 10 AxesOrigin permette di specificare in che punto gli assi cartesiani devono incrociarsi. Può infatti capitare (come ad esempio visto in alcuni dei grafici precedenti) che il punto di incrocio sia diverso dall'usuale origine degli assi. cap0.nb 57 In[134]:= ListPlot@listaPunti, PlotJoined Ø True, AxesOrigin Ø 80, 0<D; 8 6 4 2 2 4 6 8 va notato come in questo caso la modifica di AxesOrigin porta a un grafico in cui una parte degli assi cartesiani non viene visualizzata. Questo è dovuto a una precisa scelta degli sviluppatori di Mathematica. Quando si vogliono visualizzare gli assi cartesiani fino al loro punto di intersezione, in casi come questo è necessario modificare anche l'opzione PlotRange: In[135]:= ListPlot@listaPunti, PlotJoined Ø True, AxesOrigin Ø 80, 0<, PlotRange Ø 880, 9<, 80, 10<<D; 10 8 6 4 2 2 4 6 8 PlotLabel e AxesLabel permettono di specificare eventuali valori con cui etichettare l'intero grafico e gli assi cartesiani, rispettivamente, inserendo nel secondo caso i due valori in una lista. Nel caso non si volesse specificare l'etichetta per un particolare asse, è sufficiente utilizzare il valore None: 58 cap0.nb In[136]:= ListPlot@listaPunti, PlotJoined Ø True, AxesOrigin Ø 80, 0<, PlotRange Ø 880, 9<, 80, 10<<, AxesLabel Ø 8x, None<, PlotLabel -> "Una sequenza"D; Una sequenza 10 8 6 4 2 2 4 6 8 x Axes e Frame sono opzioni settabili a True o False, che determinano qualora il grafico debba visualizzare anche gli assi cartesiani e una cornice che lo contorni. In[137]:= ListPlot@listaPunti, PlotJoined Ø True, PlotLabel -> "Una sequenza", Axes Ø False, Frame Ø TrueD; Una sequenza 8 6 4 2 2 4 6 8 AspectRatio determina quale sarà il rapporto tra l'altezza e la larghezza del grafico cap0.nb 59 In[138]:= ListPlot@listaPunti, PlotJoined Ø True, PlotLabel -> "Una sequenza", Axes Ø False, Frame Ø True, AspectRatio Ø 0.4D; Una sequenza 8 6 4 2 2 4 6 8 il valore predefinito per AspectRatio è 1/GoldenRatio, il che significa che alcuni grafici possono risultare "schiacciati". Il valore Automatic, corrispondente a 1, permette di generare grafici in cui larghezza e altezza hanno le stesse unità di misura. Ticks permette di specificare i punti sugli assi di cui sarà visualizzato il relativo valore. E' necessario indicare una lista contenente due liste, ognuna indicante i valori da visualizzare rispettivamente sull'asse delle ascisse e delle ordinate. Anche in questo caso è possibile utilizzare None o Automatic per indicare che non si vogliono visualizzare valori o che si lascia a Mathematica la scelta. In[139]:= ListPlot@listaPunti, PlotJoined Ø True, AxesLabel Ø 8x, y<, Ticks Ø 881, 3, 5, 7, 9<, 82, 3, 4<<, AxesOrigin Ø 81, 1<D; y 4 3 2 3 5 7 9 x FrameTicks ha un comportamento analogo a Ticks, ma agisce sui bordi della cornice. Pertanto la sua specificazione comprende quattro liste al posto di due. 60 cap0.nb In[140]:= ListPlot@listaPunti, PlotJoined Ø True, PlotLabel -> "Una sequenza", Axes Ø False, Frame Ø True, FrameTicks Ø 88Pi, 2 Pi<, Automatic, None, None<D; Una sequenza 8 6 4 2 p 2p Attenzione! I valori specificati in Ticks e FrameTicks possono essere anche numeri reali, come in quest'ultimo esempio. In alcuni casi queste diretttive vengono usate come valore che modifica l'opzione PlotStyle, racchiudendole tra parentesi graffe nel caso si voglia indicare più di una direttiva. Tra le direttive utilizzabili per ListPlot, le più frequentemente utilizzate sono PointSize, che indica quale debba essere il diametro dei punti utilizzati (se non viene attivata l'opzione PlotJoined) In[141]:= listaPunti = 883, 1<, 87, 5<, 88, 3<, 89, 9<, 81, 4<, 82, 8<<; ListPlot@listaPunti, PlotStyle Ø [email protected]; 8 6 4 4 6 8 cap0.nb 61 Attenzione! Il valore specificato per il diametro va espresso come frazione rispetto all'ampiezza complessiva del grafico. E' quindi opportuno specificare valori molto piccoli. GrayLevel permette di specificare il colore del grafico all'interno di una scala di grigi, indipendentemente dal fatto che questo sia stato prodotto utilizzando dei punti o una linea. Il suo argomento è un numero tra 0 e 1, dove 0 indica il nero e 1 il bianco. In[162]:= listaPunti = 883, 1<, 87, 5<, 88, 3<, 89, 9<, 81, 4<, 82, 8<<; ListPlot@listaPunti, PlotStyle Ø [email protected], [email protected]<D; ListPlot@listaPunti, PlotStyle Ø [email protected], PlotJoined Ø TrueD; 8 6 4 4 6 8 4 6 8 8 6 4 RGBColor permette di specificare il colore del grafico indicando le corrispondenti componenti nei colori fondamentali rosso, verde e blu. Questo viene fatto specificando per ognuna componente un numero tra 0 e 1. 62 cap0.nb In[165]:= ListPlot@listaPunti, PlotStyle Ø [email protected], 0, 0.5D, PlotJoined Ø TrueD; 8 6 4 4 6 8 Dashing permette di specificare come tratteggiare le linee tracciate. Il suo argomento è una lista che indica la lunghezza di segmenti consecutivi: il primo numero indica quanto deve essere lungo il primo segmento, il secondo quanto deve essere lungo il primo spazio, il terzo quanto deve essere lungo il secondo segmento e così via. Quando la lista è terminata si ricomincia a utilizzarne il primo valore. Tutti i valori sono indicati rispetto all'ampiezza totale del grafico. In[182]:= ListPlot@listaPunti, PlotStyle Ø [email protected]<D, PlotJoined Ø TrueD; ListPlot@listaPunti, PlotStyle Ø [email protected], 0.02, 0.08<D, PlotJoined Ø TrueD; 8 6 4 4 6 8 cap0.nb 63 8 6 4 4 6 8 Thickness permette di specificare lo spessore delle linee tracciate, rispetto all'ampiezza totale del grafico. In[185]:= ListPlot@listaPunti, PlotStyle Ø [email protected], PlotJoined Ø TrueD; 8 6 4 4 6 8 1.21. Oggetti di tipo grafico Mathematica è in grado di generare altri tipi di grafici oltre a insiemi di punti e linee spezzate. In particolare è possibile costruire degli oggetti di tipo grafico contenenti una o più istanze dei seguenti oggetti fondamentali: Point[{x,y}] ritorna un punto le cui coordinate sono contenute nella lista usata come argomento. Line[{{x1,y1},...,{xn,yn}}] ritorna una linea spezzata i cui segmenti sono delimitati dai punti contenuti nella lista usata come argomento; in particolare quando questa lista contiene solo due punti viene generato un segmento. Circle[{x,y},r] ritorna un cerchio i cui centro e raggio sono indicati rispettivamente dal primo e secondo argomento usati. 64 cap0.nb La funzione Graphics trasforma questi oggetti in oggetti grafici visualizzabili tramite Show: In[207]:= a = Line@880, 1<, 81, 3<, 82, 5<<D; b = Graphics@aD; Show@bD; Va evidenziato che i grafici generati non includono gli assi cartesiani e fanno riferimento al valore predefinito di AspectRatio. Quest'ultimo aspetto risulta più evidente quando si visualizza un cerchio: In[210]:= a = Circle@80, 0<, 1D; b = Graphics@aD; Show@bD; in questo caso otteniamo qualcosa che ha un aspetto diverso da quello che ci saremmo aspettati: proprio per il fatto che le distanze lungo i due assi cartesiani vengono misurate utilizzando unità differenti, il cerchio appare "schiacciato" in un'ellissi. Per riparare a questo inconveniente è sufficiente impostare l'opzione AspectRatio al valore Automatic: cap0.nb In[213]:= 65 a = Circle@80, 0<, 1D; b = Graphics@aD; Show@b, AspectRatio Ø Automatic, Axes Ø TrueD; 1 0.5 -1 -0.5 0.5 1 -0.5 -1 In realtà Mathematica mette a disposizione anche altri tipi fondamentali di oggetti grafici. Fate riferimento al manuale per scoprire quali sono. Per generare un grafico contenente più oggetti fondamentali si possono seguire due strade: generare singolarmente i vari oggetti e poi mostrarli assieme in un'unica chiamata alla funzione Show: 66 cap0.nb In[216]:= linea = Graphics@Line@880, 0<, 81, 1<<DD; cerchio = Graphics@Circle@81 ê 2, 1 ê 2<, 1 ê 2DD; Show@linea, cerchio, AspectRatio Ø AutomaticD; generare i vari oggetti tramite la stessa istruzione Graphics, che può accettare come argomento una lista di oggetti fondamentali: cap0.nb In[219]:= 67 Show@Graphics@8Line@880, 0<, 81, 1<<D, Circle@81 ê 2, 1 ê 2<, 1 ê 2D<D, AspectRatio Ø AutomaticD; E' infine possibile generare oggetti specificando valori non predefiniti per l'opzione PlotStyle, anche se in modo differente rispetto a quanto visto per ListPlot: in questo caso è necessario specificare in modo differente l'argomento passato a Graphics, che ora consterà di una lista contenente i valori per PlotStyle seguiti dall'oggetto o dagli oggetti che si vogliono disegnare 68 In[228]:= cap0.nb Show@Graphics@[email protected]<D, [email protected], Circle@80, 0<, 1D<D, AspectRatio Ø AutomaticD; Nel caso si voglia produrre un grafico in cui a oggetti differenti sono associati diversi valori per le opzioni di PlotStyle, è sufficiente utilizzare come argomento di Graphics una lista innestata, i cui elementi sono le singole liste contenenti le opzioni di PlotStyle: cap0.nb In[234]:= 69 Show@Graphics@88RGBColor@0, 1, 0D, [email protected], Line@880, 0<, 80, 1<, 81 ê 2, Sqrt@3D ê 2<, 80, 0<<D<, [email protected]<D, [email protected], Circle@80, 0<, 1D<<D, AspectRatio Ø AutomaticD; 1.22. Visualizzare grafici di quantità continue Mentre la funzione ListPlot ha lo scopo di tracciare il grafico di un insieme discreto di punti, la funzione Plot ritorna un oggetto di tipo grafico che approssima il grafico di una funzione continua nei suoi argomenti. Il suo formato è Plot@espressione, rangeD dove espressione indica un'espressione, che dipende in modo continuo da una variabile simbolica, di cui si vuole disegnare il grafico, e range indica il range della variabile relativamente al grafico stesso, espresso nella forma {variabile, inizio fine}, molto simile a quella di un iteratore. 70 In[145]:= cap0.nb Plot@x, 8x, 0, 2 Pi<D; 6 5 4 3 2 1 1 2 3 4 5 6 Qualora il primo argomento di Plot sia una lista di espressioni, i relativi grafici vengono visualizzati nel medesimo oggetto di tipo grafico: In[147]:= Plot@8x, x ^ 2<, 8x, 0, 2<D; 4 3 2 1 0.5 1 1.5 2 E' necessario porre molta attenzione ai grafici prodotti da Plot. Se ad esempio si prova a visualizzare il 2 grafico di e-x tra -5 e 5 cap0.nb In[155]:= 71 Plot@E ^ H-x ^ 2L, 8x, -5, 5<D; 0.3 0.25 0.2 0.15 0.1 0.05 -4 -2 2 4 si ottiene un grafico troncato nella parte centrale. Questo è dovuto all'algoritmo che Mathematica utilizza per produrre l'output. L'oggetto grafico ritornato da Plot è infatti una linea spezzata ottenuta nel seguente modo: l'intervallo su cui graficare la funzione viene diviso in parti di uguale lunghezza, dove il numero di parti è specificato dal valore dell'opzione PlotPoints. In ogni punto di divisione viene valutata la funzione e viene generata una linea spezzata passante per i punti così ottenuti; ogni volta che l'angolo tra due parti di linea spezzata successive è maggiore del valore specificato nell'opzione MaxBend, gli intervalli corrispondenti per la variabile indipendente vengono divisi in due e viene riapplicato l'algoritmo; questo algoritmo adattivo viene applicato in modo che un intervallo non venga suddiviso in due per più del numero di volte specificato nell'opzione PlotDivision (per evitare di suddividere all'infinito segmenti al cui interno cade un asintoto verticale). E' quindi per questo motivo che il grafico sopra visualizzato non è completo: l'algoritmo di suddivisione ha erroneamente inferito che nella parte centrale vi fosse un asintoto. In questo caso è possibile specificare manualmente un valore per l'opzione PlotRange 72 In[156]:= cap0.nb Plot@E ^ H-x ^ 2L, 8x, -5, 5<, PlotRange Ø 80, 1<D; 1 0.8 0.6 0.4 0.2 -4 -2 2 4 in alternativa è possibile specificare un valore abbastanza basso per l'opzione MaxBend In[178]:= Plot@E ^ H-x ^ 2L, 8x, -5, 5<, MaxBend Ø .1D; 1 0.8 0.6 0.4 0.2 -4 -2 2 4 E' possibile visualizzare quali sono i punti usati da Plot per campionare la funzione? Intuitivamente la risposta dovrebbe essere sì, a patto di poter estrarre informazioni sulla linea spezzata che approssima il grafico. Se salviamo l'oggetto grafico prodotto e andiamo a guardarne i contenuti otteniamo cap0.nb In[179]:= 73 graph = Plot@x, 8x, 0, 1<D; Length@graphD 1 0.8 0.6 0.4 0.2 0.2 Out[180]= In[181]:= Out[181]= In[182]:= Out[182]= 0.4 0.6 0.8 1 2 graph@@0DD Graphics graph@@1DD [email protected] µ 10-8 , 4.16667 µ 10-8 <, 80.040567, 0.040567<, 80.0848088, 0.0848088<, 80.126359, 0.126359<, 80.166318, 0.166318<, 80.208853, 0.208853<, 80.249795, 0.249795<, 80.293313, 0.293313<, 80.335239, 0.335239<, 80.375574, 0.375574<, 80.418484, 0.418484<, 80.459802, 0.459802<, 80.499529, 0.499529<, 80.541831, 0.541831<, 80.582542, 0.582542<, 80.625827, 0.625827<, 80.667521, 0.667521<, 80.707624, 0.707624<, 80.750302, 0.750302<, 80.791388, 0.791388<, 80.835049, 0.835049<, 80.877119, 0.877119<, 80.917597, 0.917597<, 80.96065, 0.96065<, 81., 1.<<D<< 74 In[183]:= Out[183]= cap0.nb graph@@2DD 1 9PlotRange Ø Automatic, AspectRatio Ø ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ , GoldenRatio DisplayFunction ß $DisplayFunction, ColorOutput Ø Automatic, Axes Ø Automatic, AxesOrigin Ø Automatic, PlotLabel Ø None, AxesLabel Ø None, Ticks Ø Automatic, GridLines Ø None, Prolog Ø 8<, Epilog Ø 8<, AxesStyle Ø Automatic, Background Ø Automatic, DefaultColor Ø Automatic, DefaultFont ß $DefaultFont, RotateLabel Ø True, Frame Ø False, FrameStyle Ø Automatic, FrameTicks Ø Automatic, FrameLabel Ø None, PlotRegion Ø Automatic, ImageSize Ø Automatic, TextStyle ß $TextStyle, FormatType ß $FormatType= vediamo come l'oggetto ritornato sia in realtà una lista (rappresentante, come sapevamo, un oggetto grafico) il cui primo elemento è proprio la linea spezzata che ci interessa, e il cui secondo elemento è un riepilogo delle opzioni grafiche da applicare. Sarà quindi sufficiente estrarre gli argomenti della funzione List contenuta nel primo elemento e usare ognuno di essi come argomento di funzioni Point. La funzione Map ci permette di fare tutto ciò in modo agevole. In[184]:= plotPoints = Graphics@ Join@8AbsolutePointSize@3D<, Map@Point, graph@@1DD@@1DD@@1DD@@1DDDDD; Show@graph, plotPointsD; 1 0.8 0.6 0.4 0.2 0.2 0.4 0.6 0.8 1 per poter studiare il comportamento di Plot conviene definire una funzione plotWithPoints che abbia lo stesso formato di Plot ma che visualizzi anche i punti su cui si basa il grafico prodotto: In[186]:= plotWithPoints@f_, range_D := Block@8graph<, graph = Plot@f, rangeD; points = Graphics@ Join@8AbsolutePointSize@3D<, Map@Point, graph@@1DD@@1DD@@1DD@@1DDDDD; Show@graph, pointsD D cap0.nb In[187]:= 75 plotWithPoints@x, 8x, 0, 1<D; 1 0.8 0.6 0.4 0.2 0.2 0.4 0.6 0.8 1 0.2 0.4 0.6 0.8 1 1 0.8 0.6 0.4 0.2 Nonostante la funzione assolva allo scopo che ci eravamo prefissi, ha un fastidioso effetto collaterale: quando al suo interno viene chiamata Plot, il grafico corrispondente viene sempre visualizzato. Per evitare la visualizzazione è possibile ridefinire plotWithPoints facendo uso dell'opzione DisplayFunction, specificabile sia in Plot che in Show: quando questa è settata a Identity l'oggetto di tipo grafico viene prodotto ma non visualizzato, mentre quando è settata a $DisplayFunction l'oggetto viene anche visualizzato. E' inoltre opportuno permettere la specificazione di altre opzioni: In[193]:= Clear@plotWithPointsD; plotWithPoints@f_, range_, opts___D := Block@8graph<, graph = Plot@f, range, opts, DisplayFunction Ø IdentityD; points = Graphics@ Join@8AbsolutePointSize@3D<, Map@Point, graph@@1DD@@1DD@@1DD@@1DDDDD; Show@graph, points, DisplayFunction Ø $DisplayFunctionD D 76 In[195]:= cap0.nb plotWithPoints@x, 8x, 0, 1<D; 1 0.8 0.6 0.4 0.2 0.2 0.4 0.6 0.8 1 In questo caso il numero di punti iniziali è sufficiente, in quanto graficando una linea retta i vincoli sugli angoli tra segmenti successivi sono automaticamente rispettati. Il grafico non sarebbe cambiato nel caso avessimo usato un valore basso per PlotPoints: In[202]:= plotWithPoints@x, 8x, 0, 1<, PlotPoints Ø 3D; 1 0.8 0.6 0.4 0.2 0.2 0.4 0.6 0.8 1 Le cose cambiano se invece si visualizza una funzione non lineare: cap0.nb In[171]:= 77 plotWithPoints@Sin@xD, 8x, 0, 2 Pi<D; 1 0.5 1 2 3 4 5 6 -0.5 -1 In[200]:= plotWithPoints@Sin@xD, 8x, 0, 2 Pi<, PlotPoints Ø 2D; 1 0.5 1 -0.5 -1 2 3 4 5 6 78 In[176]:= cap0.nb plotWithPoints@E ^ H-x ^ 2L, 8x, -5, 5<, MaxBend Ø 100D; 1 0.8 0.6 0.4 0.2 -4 -2 2 4 1.23. Altri grafici di quantità continue ParametricPlot permette di generare il grafico di una funzione espressa in forma parametrica: In[246]:= ParametricPlot@8Sin@tD, Cos@tD<, 8t, 0, 2 Pi<, AspectRatio Ø 1, PlotPoints Ø 50D; 1 0.5 -1 -0.5 0.5 1 -0.5 -1 Anche in questo caso è necessario fare attenzione a settare opportunamente il valore dell'opzione PlotPoints: cap0.nb In[266]:= 79 a = ParametricPlot@t 8Sin@tD, Cos@tD<, 8t, 0, 50 Pi<, AspectRatio Ø Automatic, DisplayFunction Ø IdentityD; b = ParametricPlot@t 8Sin@tD, Cos@tD<, 8t, 0, 50 Pi<, AspectRatio Ø Automatic, PlotPoints Ø 20, DisplayFunction Ø IdentityD; Show@GraphicsArray@8a, b<DD; 150 150 100 100 50 50 -150 -100-50 -50 50 100150 -150 -100-50 -50 -100 -100 -150 -150 50 100150 In questo caso abbiamo usato la funzione GraphicsArray che permette di visualizzare più oggetti grafici disponendoli in righe e/o in colonne. La funzione Plot3D funziona in modo analogo a Plot, con la differenza che permette di graficare funzioni che dipendono da due argomenti. Anche in questo caso è necessario verificare che l'algoritmo di campionamento che produce il grafico si comporti in modo corretto, modificando i valori per PlotRange e/o PlotPoints nel caso si ottenesse un risultato falsato: In[248]:= Plot3D@E ^ H-Hx ^ 2 + y ^ 2LL, 8x, -5, 5<, 8y, -5, 5<D; 0.004 4 0.002 2 0 0 -4 -2 -2 0 2 -4 4 80 In[269]:= cap0.nb Plot3D@E ^ H-Hx ^ 2 + y ^ 2LL, 8x, -5, 5<, 8y, -5, 5<, PlotRange Ø 80, 1<D; 1 0.8 0.6 0.4 0.2 0 4 2 0 -4 -2 -2 0 2 -4 4 In[272]:= Plot3D@E ^ H-Hx ^ 2 + y ^ 2LL, 8x, -5, 5<, 8y, -5, 5<, PlotRange Ø 80, 1<, PlotPoints Ø 40D; 1 0.8 0.6 0.4 0.2 0 4 2 0 -4 -2 -2 0 2 -4 4 ContourPlot e DensityPlot generano grafici contenenti rispettivamente le curve di livello e i valori di densità di una funzione di due variabili: cap0.nb In[280]:= 81 a = ContourPlot@E ^ H-Hx ^ 2 + y ^ 2LL, 8x, -2, 2<, 8y, -2, 2<, DisplayFunction Ø IdentityD; b = DensityPlot@E ^ H-Hx ^ 2 + y ^ 2LL, 8x, -2, 2<, 8y, -2, 2<, DisplayFunction Ø IdentityD; Show@GraphicsArray@8a, b<DD; 2 2 1 1 0 0 -1 -1 -2 -2 -1 0 1 2 -2 -2 -1 0 1 2 1.24. Un'altra digressione: la ruota dei colori Mathematica mette a disposizione, oltre a RGBColor, un'ulteriore funzione che genera un colore. Hue permette infatti di generare colori specificando le corrispondenti componenti nella scala HSB (Hue, Saturation, Brightness) come numeri compresi tra 0 e 1. In particolare il primo di questi valori varia in modulo 1, nel senso che quando la corrispondente componente vale 0 il colore ritornato fa parte della scala del rosso e man mano che il valore aumenta, il colore si sposta prima nella scala del verde, poi in quella del blu, per ritornare infine ancora nella scala dei rossi. La seguente cella utilizza questa informazione per generare la ruota dei colori. In che modo funziona (suggerimento: ricordatevi come funziona il passaggio in coordinate polari)? In[95]:= stepRho = 100; stepTheta = 700; Show@Graphics@Table@8Hue@theta ê H2 PiL, rho, 1D, AbsolutePointSize@2D, Point@8rho Cos@thetaD, rho Sin@thetaD<D<, 8theta, 0, 2 Pi, 2 Pi ê stepTheta<, 8rho, 0, 1, 1 ê stepRho<DD, Axes Ø True, AspectRatio Ø AutomaticD; 1.25. Le parentesi Per quanto visto finora: le parentesi tonde delimitano una parte di espressione da valutare senza seguire le regole di precedenza predefinite per gli operatori matematici; le parentesi quadre delimitano gli argomenti di una funzione; le parentesi graffe delimitano il contenuto di una lista; le doppie parentesi quadre delimitano una precisa posizione all'interno di una lista. 82 cap0.nb 1.26. Programmare con Mathematica Mathematica implementa le seguenti strutture per la programmazione If[test, istruzione_true, istruzione_false] realizza la struttura di selezione tra due alternative; Switch[espressione, valore_1, istruzione_1, ...] realizza la struttura di selezione tra un numero finito di alternative, basandosi sui possibili valori assunti da un'espressione; Which[test1, istruzione_1, ...] realizza una struttura di selezione tra un numero finito di alternative; For[inizio, test, incremento, istruzione] realizza una struttura di iterazione enumerata; Do[espressione, iteratore] realizza una struttura di iterazione enumerata; While[test, istruzione] realizza una struttura di iterazione condizionata. Block[{var_locali}, istruzioni] realizza una procedura con una serie di variabili locali. E' possibile eseguire più iterazioni separandole con il carattere di punto e virgola. L'esito dell'ultima istruzione senza un carattere di punto e virgola sarà il valore ritornato dalla procedura. Verificate utilizzando l'help in linea la loro sintassi e la loro semantica e utilizzatele per scrivere dei semplici algoritmi che agiscono sulle strutture di cui abbiamo parlato. Ad esempio, verificate come l'istruzione In[188]:= Do@ListPlot@Table@Sin@2 Pi ê i jD, 8j, 1, 2 Pi, 0.01<DD, 8i, 1, 10, 0.2<D; 1 0.5 100 -0.5 -1 dia luogo a un'animazione. 200 300 400 500 cap0.nb 83 1.27. Importare ed esportare dati E' possibile importare dati esterni per elaborarli con Mathematica e, analogamente, esportare risultati di elaborazioni effettuate. Siccome per effettuare queste operazioni è spesso necessario interagire con il file system, è necessario capire dove Mathematica legge e scrive documenti su disco. Come in molti altri programmi, in assenza di informazioni specifiche viene utilizzata una directory predefinita, il cui nome viene visualizzato quando si esegue la funzione Directory In[464]:= Out[464]= Directory@D êUsersêmalchiod e impostato utilizzando la funzione SetDirectory In[467]:= SetDirectory@"êUsersêmalchiodêDesktopêDarioêEditoriaêLibriêMathematica"D; Attenzione! Ovviamente le directory indicate nei comandi precedenti fanno riferimento a directory presenti nel computer dell'autore. Pertanto se eseguite il comando senza modificarne l'argomento otterrete con alta probabilità un messaggio di errore. Il comando Import legge un file e lo interpreta in termini dell'oggetto di Mathematica più opportuno per descriverne i contenuti. Se ad esempio nella directory /Users/malchiod/ Desktop/Dario/Editoria/Libri/Mathematica è presente un file dal nome prova.csv il cui contenuto è il seguente 1, 2, 3, 4, 5 a, b, c, d, e in cui ogni riga indica una serie di dati, separati da virgole, che si riferiscono a uno stesso record (e infatti l'estensione CSV indica il formato comma separated values, cioé "valori separati da virgole"), l'esito di Import sarà In[479]:= Out[479]= Import@"prova.csv"D 881, 2, 3, 4, 5<, 8a, b, c, d, e<< e quindi ogni riga di input è stata interpretata come l'elemento di una lista; gli elementi di questa lista sono a loro volte liste che contengono tutti i valori indicati in una data riga. Nel caso si tentasse di usare Import con un file non esistente nella directory, verrà visualizzato un messaggio di errore: 84 cap0.nb Import@"QuestoFileNonEsiste.csv"D In[469]:= Import::nffil : File not found during [email protected], CSVD. More… Out[469]= $Failed in realtà in casi come questo Mathematica effettua la ricerca in una serie di directory il cui nome è contenuto nella variabile $Path: In[462]:= Out[462]= $Path 8êApplicationsêMathematica 5.0.appêAddOnsêJLink, êApplicationsêMathematica 5.0.appêAddOnsêNETLink, êUsersêmalchiodêLibraryêMathematicaêKernel, êUsersêmalchiodêLibraryêMathematicaêAutoload, êUsersêmalchiodêLibraryêMathematicaêApplications, êLibraryêMathematicaêKernel, êLibraryêMathematicaêAutoload, êLibraryêMathematicaêApplications, ., êUsersêmalchiod, êApplicationsêMathematica 5.0.appêAddOnsêStandardPackages, êApplicationsêMathematica 5.0.appêAddOnsêStandardPackagesêStartUp, êApplicationsêMathematica 5.0.appêAddOnsêAutoload, êApplicationsêMathematica 5.0.appêAddOnsêApplications, êApplicationsêMathematica 5.0.appêAddOnsêExtraPackages, êApplicationsêMathematica 5.0.appêSystemFilesêGraphicsêPackages, êApplicationsêMathematica 5.0.appêConfigurationêKernel< Attenzione! Anche in questo caso l'output del comando dipende fortemente dal particolare computer su cui è stato eseguito il notebook. Import è in grado di riconoscere le estensioni di alcuni formati comuni, oltre a quello CSV. E' comunque possibile specificare un secondo argomento per indicare esplicitamente il formato da utilizzare. In altre parole, l'istruzione In[470]:= Import@"prova.csv", "CSV"D avrebbe avuto lo stesso esito. E' possibile specificare, tra le altre, le opzioni: EmptyField, che indica come debba essere intrepretato un dato mancante, FieldSeparator, che indica quale sia il carattere che separa un dato dall'altro, e Numeric, che indica se le quantità lette che indicano un valore numerico debbano essere convertite o mantenute come stringhe. Altri formati supportati sono Lines, che ritorna una lista contenente un elemento per ogni riga del file letto cap0.nb 85 In[474]:= Out[474]= Import@"prova.csv", "Lines"D 81,2,3,4,5, a,b,c,d,e< nonostante l'output possa sembrare fuorviante, la lista ritornata contiene solo due elementi: lo possiamo evincere dal fatto che questi sono separati da una virgola seguita da uno spazio, oppure procedendo a un'ispezione diretta In[475]:= Out[475]= %@@1DD 1,2,3,4,5 List, che ritorna una lista contenente un elemento per ogni parola del file letto Table, che funziona in modo simile a List, ma ritorna una lista bidimensionale che per ogni riga del file letto contiene un elemento, che a sua volta è una lista composta da tutte le parole contenute nella riga. TSV, che funziona in modo simile a CSV, ma che separa i dati utilizzando il carattere di tabulazione al posto della virgola. In realtà Import riconosce anche tipi di dati non necessariamente numerici o simbolici In[486]:= Out[486]= In[487]:= Import@"mela.gif"D Ü Graphics Ü Show@%D; 86 In[489]:= cap0.nb logo = Import@"x.jpg"D; Show@logoD; L'elenco dei formati riconosciuti è lungo, e comprende formati per il suono, per l'output di particolari programmi che manipolano espressioni e dati numerici, e per gestire dati descritti tramite XML. Per un elenco dettagliato fate riferimento al manuale. Utilizzando la funzione ImportString è infine possibile importare dati non da un file esterno, ma interpretando il contenuto di una stringa In[496]:= Out[496]= ImportString@"1,2,3,4,5\n6,7,8,9", "CSV"D 881, 2, 3, 4, 5<, 86, 7, 8, 9<< E' altresì possibile effettuare l'operazione inversa di esportazione, cioé esportare il risultato di una computazione di Mathematica. In questo caso si utilizzano le funzioni Export ed ExportString, che seguono lo stesso formato delle corrispondenti funzioni di importazione. In[498]:= Out[498]= ExportString@Table@i, 8i, 1, 4<, 8j, 1, 2<D, "CSV"D 1,1 2,2 3,3 4,4 cap0.nb 87 In[503]:= Out[503]= Export@"prova.jpg", Graphics@Circle@80, 0<, 1DD, "JPG"D prova.jpg Nel caso si volessero importare in un notebook dei dati che non seguono alcun formato tra quelli supportati, è possibile tentare di trasformarli ad esempio nel formato CSV utilizzando le funzioni di sostituzione automatica di un editor di testi avanzato. E' poi possibile esportare il risultato di una valutazione in formati utilizzabili da altri linguaggi: ad esempio TeXForm e CForm producono in output l'esito dell'espressione passata come argomento espresso in una forma utilizzabile rispettivamente in TEX e in C: In[518]:= Sqrt@x z + 1 ê yD êê TeXForm Out[518]//TeXForm= {\sqrt{\frac{1}{y} + x\,z}} In[517]:= Sqrt@x z + 1 ê yD êê CForm Out[517]//CForm= Sqrt(1/y + x*z) E' infine da segnalare la possibilità di esportare un intero notebook in formato LATEX, HTML e MATHML, utilizzando la voce di menù File/Save As Special. 1.28. Leggere e scrivere su file Nella sezione precedente abbiamo visto come Mathematica sia in grado di importare ed esportare dati utilizzando vari formati di codifica. Spesso è utile invece poter effettuare delle semplici operazioni di lettura e scrittura su disco, ad esempio per salvare in modo permanente l'esito di un'operazione complessa per poi rileggerlo in una sessione successiva. I comandi Get e Put, rispettivamente abbreviati con le sequenze << e >> permettono di effettuare operzioni di questo tipo. Più precisamente: >> è un operatore preceduto da un'espressione e seguito dal nome di un file. Quando la cella che lo contiene viene eseguita, l'espressione viene valutata ed il valore ottenuto viene scritto nel file. Il file risiederà nella directory corrente, ed eventuali suoi contenuti verranno cancellati. L'operatore PutAppend, abbreviabile tramite la sequenza >>>, ha lo stesso effetto di >> con la sola differenza che l'output viene scritto in coda a eventuali contenuti del file. In[505]:= Table@i, 8i, 1, 10<D >> "provoutput" << è un operatore seguito dal nome di un file. Quando la cella che lo contiene viene eseguita, il contenuto del file viene inserito nella cella stessa al posto dell'operatore e del nome del file, e successivamente la cella viene valutata. 88 cap0.nb In[506]:= Out[506]= In[513]:= Out[514]= << "provoutput" 81, 2, 3, 4, 5, 6, 7, 8, 9, 10< tabellaLetta = << "provoutput"; tabellaLetta 81, 2, 3, 4, 5, 6, 7, 8, 9, 10< 1.29. Esercizi 1.29.1. Implementare la serie di Fibonacci In[191]:= fib@n_D := fib@n - 1D + fib@n - 2D; fib@1D = 1; fib@0D = 1; In[194]:= Trace@fib@3DD Out[194]= 8fib@3D, fib@3 - 1D + fib@3 - 2D, 883 - 1, 2<, fib@2D, fib@2 - 1D + fib@2 - 2D, 882 - 1, 1<, fib@1D, 1<, 882 - 2, 0<, fib@0D, 1<, 1 + 1, 2<, 883 - 2, 1<, fib@1D, 1<, 2 + 1, 3< 1.29.2. Implementare la sostituzione per la somma di logaritmi In[91]:= Out[92]= log@x_ y_D := log@xD + log@yD; Trace@log@a b c dDD 8log@a b c dD, log@aD + log@b c dD, 8log@b c dD, log@bD + log@c dD, 8log@c dD, log@cD + log@dD<, log@bD + Hlog@cD + log@dDL, log@bD + log@cD + log@dD<, log@aD + Hlog@bD + log@cD + log@dDL, log@aD + log@bD + log@cD + log@dD< cap0.nb 89 1.29.3. Implementare l'algoritmo per le frazioni egiziane Il problema della scomposizione in frazioni egiziane è il seguente: data una frazione minore di 1, scomporla come la somma di frazioni aventi il numeratore pari a uno. Il seguente approccio porta a una scomposizione di questo tipo: partendo dal numero q, la più grande frazione avente forma 1/m che risulta essere minore o uguale a q è tale che m è il più piccolo intero maggiore di 1/q, che risulta essere Ceiling[1/q]; trovata tale funzione, è sufficiente reiterare il procedimento sulla quantità residua q-1/m. Verificare come il seguente codice implementi questo approccio In[203]:= In[209]:= Out[209]= greedyEF@0D := 8<; greedyEF@q_D := Prepend@greedyEF@q - 1 ê Ceiling@1 ê qDD, Ceiling@1 ê qDD ê; q < 1 greedyEF@2 ê 3D 82, 6< quale è il significato del contenuto della lista? Suggerimento: la seguente istruzione verifica che il risultato sia corretto In[210]:= Out[210]= Apply@Plus, 1 ê %D 2 ÅÅÅÅ 3 Verificare il comportamento dell'algoritmo sulle frazioni aventi la forma 1-10^(-i), dove i è un intero positivo da far variare. 1.29.4. Frazioni continue e rapporti aurei La funzione NestList costruisce una lista nel seguente modo: considera un valore di partenza (il primo della lista), e gli applica una funzione ottenendo un valore (il secondo della lista). A questo valore applica nuovamente la funzione, ottenendo un nuovo valore (il terzo della lista) e così via, per un numero di volte determinato: In[213]:= Out[213]= NestList@f, x, 10D 8x, f@xD, f@f@xDD, f@f@f@xDDD, f@f@f@f@xDDDD, f@f@f@f@f@xDDDDD, f@f@f@f@f@f@xDDDDDD, f@f@f@f@f@f@f@xDDDDDDD, f@f@f@f@f@f@f@f@xDDDDDDDD, f@f@f@f@f@f@f@f@f@xDDDDDDDDD, f@f@f@f@f@f@f@f@f@f@xDDDDDDDDDD< 90 cap0.nb Applicando NestList alla funzione 1/(1+x) si ottiene un oggetto matematico chiamato funzione continua: In[214]:= Out[215]= f@x_D := 1 ê H1 + xL; NestList@f, x, 6D 1 1 1 1 1 1 9x, ÅÅÅÅÅÅÅÅÅÅÅÅ , ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅÅÅ , ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅÅ 1 ÅÅ , ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ 1ÅÅÅÅÅÅÅ , ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ 1 1 ÅÅÅÅÅ , ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ 1 ÅÅÅÅÅÅÅÅÅ = 1+x 1 + ÅÅÅÅÅÅÅ 1 + ÅÅÅÅÅÅÅÅ Å ÅÅ Å 1 + ÅÅÅÅÅÅÅÅ Å ÅÅÅÅÅÅ 1 + ÅÅÅÅÅÅÅÅ Å ÅÅÅÅÅÅÅ Å Å 1 + ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ 1 1 1 1 ÅÅÅÅÅÅ 1+x 1+ ÅÅÅÅÅÅÅÅ 1+ ÅÅÅÅÅÅÅÅÅÅÅÅÅ 1+ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ 1+ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ ÅÅÅÅÅÅÅ 1+x 1 1+ ÅÅÅÅ ÅÅÅÅÅ 1+x 1 1+ ÅÅÅÅÅÅÅÅ ÅÅÅÅ 1ÅÅÅÅÅ 1+ ÅÅÅÅÅÅÅÅÅÅÅÅ 1+x 1 1+ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ 1ÅÅÅÅÅÅÅÅ 1+ ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ 1 1+ ÅÅÅÅÅÅÅÅÅÅÅÅ 1+x Cosa succede se invece specifichiamo un valore preciso come punto di partenza? In[216]:= Out[216]= NestList@f, 1, 20D 1 2 3 5 91, ÅÅÅÅ , ÅÅÅÅ , ÅÅÅÅ , ÅÅÅÅ , 2 3 5 8 233 377 610 ÅÅÅÅÅÅÅÅÅÅ , ÅÅÅÅÅÅÅÅÅÅ , ÅÅÅÅÅÅÅÅÅÅ , 377 610 987 8 13 21 34 55 89 144 ÅÅÅÅÅÅÅ , ÅÅÅÅÅÅÅ , ÅÅÅÅÅÅÅ , ÅÅÅÅÅÅÅ , ÅÅÅÅÅÅÅ , ÅÅÅÅÅÅÅÅÅÅ , ÅÅÅÅÅÅÅÅÅÅ , 13 21 34 55 89 144 233 987 1597 2584 4181 6765 10946 ÅÅÅÅÅÅÅÅÅÅÅÅÅ , ÅÅÅÅÅÅÅÅÅÅÅÅÅ , ÅÅÅÅÅÅÅÅÅÅÅÅÅ , ÅÅÅÅÅÅÅÅÅÅÅÅÅ , ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ , ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ = 1597 2584 4181 6765 10946 17711 Considerate la sequenza dei numeratori e dei denominatori delle frazioni: non notate nulla di strano? Se invece consideriamo la stessa lista in formato approssimato otteniamo In[217]:= Out[217]= NestList@f, 1, 20D êê N 81., 0.5, 0.666667, 0.6, 0.625, 0.615385, 0.619048, 0.617647, 0.618182, 0.617978, 0.618056, 0.618026, 0.618037, 0.618033, 0.618034, 0.618034, 0.618034, 0.618034, 0.618034, 0.618034, 0.618034< Sembra che la successione definita dagli elementi della lista tenda a un valore limite. Argomentate questa asserzione nel modo più convincente possibile e tentate di stabilire una connessione tra questo limite e il valore della costante In[219]:= Out[219]= GoldenRatio êê N 1.61803
Documenti analoghi
Introduzione a Mathematica
calcolo numerico e simbolico è alla sua base. Per calcolo numerico di solito intendiamo semplici operazioni
aritmetiche. Ad esempio:
01-Introduzione a Mathematica.nb
Quando si hanno più celle in un gruppo si possono nascondere alcune celle, ad esempio il codice per generare un grafico
Plot@Cos@xD, 8x, -5, 5