Mathematica: uno sguardo più in profondità

Giulio Cesare Barozzi
Università di Bologna

[Revisione: luglio 2001]

Le espressioni

Clear["Global`*"]

In Mathematica  qualunque scrittura grammaticalmente corretta è un'espressione.
Possiamo esaminare la struttura interna di un'espressione mediante il comando FullForm:

FullForm[a -> b]

FullForm[a :> b]

FullForm[{a,b,c}]

FullForm[a == b]

FullForm[a > b]

FullForm[f[x,y]]

FullForm[a_ -> a^2]

Se un comando provoca un'azione (come il comando di assegnazione  immediata =), il comando  FullForm  può essere insufficiente:

FullForm[x = 4]

... ma in questo caso il comando HoldForm (che inibisce tale azione) viene in nostro aiuto:

FullForm[HoldForm[x = 4]]

Lo stesso vale per il comando di assegnazione differita (cioè := ):

FullForm[y := 5]

FullForm[HoldForm[y := 5]]

Altro esempio:

FullForm[HoldForm[f[x_] := x^2]]

Una funzione pura:

FullForm[(#+1)&]

... applicata ad una lista:

Map[(#+1)&, Range[4]]

FullForm[HoldForm[Map[(#+1)&, Range[4]]]]

FullForm[Map[(#+1)&, Range[4]]]

Ripuliamo tutte le variabili ed introduciamo un'espressione:

Clear["Global`*"]

expr1 = x + (y+z)^2;

FullForm[expr1]

Ed ecco il comando TreeForm:

TreeForm[%]

Per ottenere l'intestazione (Head) di un'espressione si può usare la parte di indice 0:

expr1[[0]]

... oppure il comando Head:

Head[expr1]

L'intestazione corrisponde dunque alla radice dell'albero che rappresenta l'espressione.

expr2 = a+b+c;

FullForm[expr2]

TreeForm[expr2]

Poiché l'addizione è associativa, dalla radice Plus escono tre rami, uno per ogni addendo. La profondità di un'espressione comprende anche la sua intestazione, dunque è un'unità in più dei livelli che vengono stampati:

Depth[expr1]

Depth[expr2]

Un'espressione atomica è un'espressione che non ha sub-espressioni; la sua profondità e 1:

FullForm[a]

a[[0]]

Depth[a]

Altre espressioni atomiche (usiamo la funzione Head in forma postfissa):

5//Head

3.1//Head

Possiamo usare il predicato AtomQ, applicato ai singoli elementi di una lista mediante il comando Map:

Map[AtomQ, {2, 3.1, a}]

I quozienti richiedono un'attenzione particolare:

expr3 = x + y/x;

FullForm[%]

TreeForm[%]

Verifichiamo in quali posizioni si trova x:

Position[expr3,x]

Consideriamo la funzione pura (#+1)&, cioè quella che somma 1 al proprio argomento: se usiamo il comando Map, essa agisce su expr3 come se tale espressione fosse una lista:

Map[(#+1)&, expr3]

Per capire che cosa è accaduto, trasformiamo prima expr3 in una vera e propria lista mediante il comando Apply:

Apply[List, expr3]

Map[(#+1)&, %]

Diamo nuovamente a quanto ottenuto l'intestazione originaria (Plus):

Apply[Plus, %]

E' esattamente quanto ottenuto applicando direttamente Map.

Facciamola agire sull'elemento di indice 1:

MapAt[(#+1)&, expr3, {1}]

Facciamola agire sugli elementi di indici 1 e 2:

MapAt[(#+1)&, expr3, {{1},{2}}]

Poiché expr3, pensata come lista, ha esattamente due elementi, otteniamo lo stesso risultato di Map, puro e semplice. Facciamo agire la nostra funzione pura sulla x, cioè sugli elementi di indici {1} e {2,1,1} di expr3:

MapAt[(#+1)&, expr3, {{1},{2,1,1}}]

Abbiamo ottenuto quanto desiderato. Anche se matematicamente insensato, andiamo a vedere il risultato del comando MapAll:

MapAll[(#+1)&, expr3]

Riprendiamo in esame la forma ad albero della nostra espressione:

TreeForm[expr3]

Cominciando dal fondo, si comincia col calcolare (x+1) elevato a (-1+1), ottenendo 1; a questo risultato di somma 1 e lo si moltiplica per y+1, ottenendo 2(1+y); a questo si somma 1 e lo si aggiunge ad x+1; finalmente, giunti al livello 0, si somma un ultimo 1. Si ottiene quanto mostrato in precedenza.

Supponiamo di voler aumentare di un'unità numeratore e denominatore di una frazione algebrica. Andiamo a vedere quali posizioni occupano il numeratore e il denominatore di una frazione algebrica:

expr4 = a/b;

Position[%,a]

Position[%%,b]

Supponiamo di voler aumentare di un'unità numeratore e denominatore:

MapAt[(#+1)&, expr4, {{1},{2,1}}]

MapAt[(#+1)&, (x+y)/z, {{1},{2,1}}]

Se proviamo con una frazione numerica, abbiamo una delusione:

MapAt[(#+1)&, 3/4, {{1},{2,1}}]

Esaminiamo la frazione:

FullForm[3/4]

Depth[3/4]

Dunque le frazioni numeriche sono espressioni atomiche:

AtomQ[3/4]

.... così come lo sono i numeri complessi (meglio: le espressioni complesse):

AtomQ[3 + 2 I]

Head[3+2 I]

Ecco una prima maniera di aumentare numeratore e denominatore di una frazione numerica:

MapAt[(#+1)&, a/b, {{1},{2,1}}]/.{a -> 3, b -> 4}

Una seconda maniera, più interessante, usa il comando HoldForm:

FullForm[HoldForm[3/4]]

TreeForm[HoldForm[3/4]]

Cerchiamo le posizioni occupate da numeratore e denominatore:

Position[%,3]

Position[%%,4]

Applichiamo la nostra funzione nelle posizioni giuste:

MapAt[(#+1)&, HoldForm[3/4], {{1,1}, {1,2,1}}]

... e finalmente “rilasciamo" il comando Hold:

ReleaseHold[%]