• Non ci sono risultati.

I metodi di tipo post e dump

Nel documento Introduzione a SuperCollider (pagine 38-46)

[1,2,3,7,5] reverse [5,7,3,2,1] reverse [1,2,3,7,5]

[1,2,3,7,5] class Array superclass ArrayedCollection

[ class Polynomial, class RawArray, class Array ]

subclasses

Array

[2]

[ nil, nil, nil ]

newClear(3)

Fig. 2.5 Due esempi di concatenamento.

2.5 I metodi di tipo post e dump

Si è detto che tutti i metodi restituiscono un oggetto. A scanso di equivoci va ricordato il comportamento dei metodi che permettono di ottenere infor- mazioni attraverso la post window. Esempi già visti sono dumpClassSubtree e

dumpSubclassList, e dumpInterface, dumpFullInterface, dumpMethodList. Il metodo più usato per ottenere generiche informazioni su quanto sta avvenen- do è postln, che stampa una stringa di informazione relativa all’oggetto su cui è chiamato (e va a capo). Ad esempio si consideri il codice Array.postln. Una volta valutata, l’espressione produce sulla post window:

1 Array 2 Array

Quando viene chiamato il metodo postln su Array, SC esegue il codice il quale prevede di stampare informazioni su Array: in questo caso, essendo Array una classe, viene semplicemente stampato il nome della classe, Array appunto (1). Infine SC stampa sempre sullo schermo un’informazione sull’ultimo ogget- to su cui è stato invocato un metodo: in sostanza SC chiama sull’ultimo oggetto proprio postln. E infatti si ottiene di nuovo Array (2). Se in questo caso l’utilità di postln è virtualmente nulla, si consideri invece il caso seguente:

1 z = [ 4, 3, 2, 1 ] ;

2 z.postln.reverse.postln.mirror.postln.mirror

3 z.postln.reverse.postln.mirror.postln.mirror.postln

La valutazione delle tre espressioni produce sulla post window i tre blocchi a stampa seguenti:

1 [ 4, 3, 2, 1 ] 3 [ 4, 3, 2, 1 ] 4 [ 1, 2, 3, 4 ] 5 [ 1, 2, 3, 4, 3, 2, 1 ] 6 [ 1, 2, 3, 4, 3, 2, 1, 2, 3, 4, 3, 2, 1 ] 8 [ 4, 3, 2, 1 ] 9 [ 1, 2, 3, 4 ] 10 [ 1, 2, 3, 4, 3, 2, 1 ] 11 [ 1, 2, 3, 4, 3, 2, 1, 2, 3, 4, 3, 2, 1 ] 12 [ 1, 2, 3, 4, 3, 2, 1, 2, 3, 4, 3, 2, 1 ]

A z è assegnato l’array [ 4, 3, 2, 1 ]. Viene quindi chiamato il conca- tenamento di metodi reverse.mirror.mirror, senonché dopo ogni metodo è concatenato anche un messaggio postln. In sostanza in questo caso postln per- mette di vedere sullo schermo il risultato intermedio restituito da ognuno dei metodi invocati. Si noti come sia inutile (nella circostanza) concatenare di nuo- vo postln dopo l’ultimo mirror (come avviene nel codice a riga 3), visto che per definizione SC stampa il risultato dell’ultima computazione (ciò che mirror re- stituisce). Infatti, le righe 11 e 12 riportano lo stesso risultato.

Ci si potrebbe aspettare che, poiché postln serve a stampare sullo schermo, ciò che questo metodo restituisce sia un oggetto di tipo stringa, un insieme di caratteri. Ma fortunatamente non è così. Infatti, postln:

• stampa una stringa sullo schermo

• restituisce l’oggetto su cui è chiamato il metodo

Le due cose sono completamente diverse. La prima è un comportamento che è indipendente dalla computazione, la seconda invece riguarda il flusso del- la computazione, perché l’oggetto restituito è disponibile per ulteriori elabora- zioni. Da quest’ultimo punto di vista (che è ciò che conta per il concatenamento dei messaggi), postln è totalmente trasparente rispetto all’oggetto su cui è chia- mato. Questo comportamento è assolutamente cruciale nella fase di correzione degli errori del codice (“debug”), perché permette di concatenare messaggi di stampa per verificare i comportamenti dei metodi invocati, ma senza per que- sto interferire con il processo. Se infatti il metodo postln restituisse una stringa,

allora in z.postln.reverse il messaggio reverse sarebbe ricevuto da un ogget- to di tipo stringa e non da un oggetto array, come nell’esempio seguente, in cui reverse è chiamato su una stringa:

1 z = [ 4, 3, 2, 1 ] ;

2 z.postln.reverse.postln.mirror.postln.mirror

3 z.postln.reverse.postln.mirror.postln.mirror.postln

Il risultato sarebbe allora: 1 ] 4 ,3 ,2 ,1 [

In altri termini, l’inversione avviene sui caratteri che compongono la stringa (si veda dopo per una spiegazione dettagliata). Questo tipo di comportamen- to vale tipicamente per tutti i metodi di stampa e di introspezione. I metodi di stampa a schermo molti: ad esempio varianti di postln sono post, postc, postcln. Per l’introspezione, si osservino gli esempi di Collection.dumpClas- sSubtree, Collection.dumpSubclassList, Array.dumpInterface. In tutti e tre i casi l’ultima riga stampa l’oggetto che il metodo restituisce: si noti come ven- ga restituita la classe, secondo quanto stampato nell’ultima riga delle rispettiva schermate (Collection, Collection, Array).

2.6 Numeri

L’interprete di SC può essere utilizzato come calcolatore. Ad esempio, si consideri questa sessione interattiva:

1 2.3*2 ; 2 4/3 ; 3 4**3 ; 4 4+2*3 ;

A cui l’interprete sulla post window risponde con: 1 4.6

2 1.3333333333333 3 64

4 18

Due cose sono di rilievo. La prima, molto (molto) importante, concerne l’or- dine di esecuzione. Se si osserva 4+2*3 si nota come, a differenza della conven- zione matematica standard, non ci sia gerarchia tra gli operatori: ad esempio, la moltiplicazione non viene valutata prima dell’addizione. Nella riga prima viene valutato 4+2, quindi *3 che viene riferito al risultato dell’operazione pre- cedente (4 + 2 = 6 × 3 = 18). L’ordine di esecuzione può essere specificato con l’uso delle parentesi, così: 4+(2*3). Il secondo aspetto che dovrebbe stupi- re il lettore è che la sintassi utilizzata contraddice il presupposto per cui in SC ogni cosa è un oggetto dotato di un’interfaccia, per cui ogni operazione dovreb- be seguire il modello generale oggetto.metodo. Qui in effetti SC fa un’eccezio- ne, almeno per le quattro operazioni, che possono essere scritte in modo più intuitivo nella consueta forma funzionale. Si noti che si tratta soltanto di una convenzione di notazione. Ciò non toglie che i numeri (interi, a virgola mobile, etc.) siano oggetti a tutti gli effetti. Se si chiama il metodo class su un intero, ad esempio 5 (1), si ottiene la classe a cui appartiene in quanto istanza: Integer. Si può allora chiamare su Integer il metodo superclasses che restituisce un array (attenzione, questa volta il metodo restituisce un array) contenente tutte le superclassi fino a Object. Valutato riga per riga, il codice:

1 5.class ;

2 Integer.superclasses ;

restituisce: 1 Integer

2 [ class SimpleNumber, class Number, class Magnitude, class Object ]

Intuibilmente, a parte Object, che è superclasse di tutto, Magnitude è la clas- se che più in generale si occupa di grandezze (tra cui i numeri). Con Magnitu- de.allSubclasses si ottiene allora:

1 [ class Association, class Number, class Char, class Polar, class Complex, 2 class SimpleNumber, class Float, class Integer ]

Con Magnitude.dumpClassSubtree si accede ad una rappresentazione ad al- bero delle sottoclassi di Magnitude: tutte le classi che si occupano di grandezze: Integer –i numeri interi– è vicino a Float –i numeri a virgola mobile–, poiché sono due sottoclassi di SimpleNumber. Quest’ultima classe fa parte del più vasto insieme delle sottoclassi di Number, i numeri in generale, compresi quelli polari e quelli complessi (Polar, Complex, che qui non interessano).

1 Magnitude.dumpClassSubtree 2 Magnitude 3 [ 4 Association 5 Number 6 [ 7 Polar 8 Complex 9 SimpleNumber 10 [ Float Integer ] 11 ] 12 Char 13 ] 14 Magnitude

In quanto oggetto (cioè, propriamente, in quanto istanza indiretta di Ob- ject), è possible inviare ai numeri, il 3 ad esempio, il messaggio postln, il quale stampa il numero e restituisce il numero stesso. Dunque il codice:

1 3.postln ;

2 3.postln * 4.postln ;

restituisce sulla post window: 1 3

2 3 3 3 4 4 5 12

Dove (1) e (2) costituiscono l’output della riga di codice (1), e (3), (4), (5) della riga di codice (2). Si ricordi quanto osservato sulla chiamata da parte dell’inter- prete di postln sull’ultima espressione del codice valutato e sulla trasparenza del metodo.

Per numerose operazioni matematiche è disponibile una doppia notazione, funzionale e ad oggetti9. 1 sqrt(2) ; 2 2.sqrt ; 4 4**2 ; 5 4.pow(2) ; 6 4.squared ; 8 4**3 ; 9 4.pow(3) ; 10 4.cubed ;

Ad esempio sqrt(2) (1) chiede di eseguire la radice quadrata di2: in no-

tazione ad oggetti si tratta di invocare su 2 il metodo sqrt (2), che restituisce il risultato dell’operazione radice quadrata applicata all’oggetto su cui è chiama- to. Analogamente, l’elevamento a potenza può essere scritto funzionalmente come 4**2 (4), oppure come 4.pow(2) (5): si chiama il metodo pow con argo- mento 2 sull’oggetto 4. Ovvero, tradotto in lingua naturale: “oggetto 4, èlevati a potenza con esponente 2”. Ancora, una terza opzione (6) consiste nell’utiliz- zare un metodo dedicato per l’elevante al quadrato, squared. Lo stesso vale per l’elevamento al cubo (8-10).

Dopo questa rapida introduzione alla programmazione ad oggetti, diventa opportuno affrontare nel dettaglio la sintassi di SC, così da scrivere un program- ma vero e proprio.

Come in ogni linguaggio, per parlare in SuperCollider è necessario segui- re un insieme di regole. Come in tutti i linguaggi di programmazione, queste regole sono del tutto inflessibili e inderogabili. In SuperCollider, un enunciato o è sintatticamente corretto o è incomprensibile all’interprete, che lo segnalerà all’utente. Questo aspetto non è esattamente amichevole per chi non è abitua- to ai linguaggi di programmazione, e si trova così costretto ad una precisione di scrittura decisamente poco “analogica”. Tuttavia, ci sono due aspetti positivi nello scrivere codice, interrelati tra loro. Il primo è lo sforzo analitico necessario, che allena ad un’analisi precisa del problema che si vuole risolvere in modo da poterlo formalizzare linguisticamente. Il secondo concerne una forma di consa- pevolezza specifica: salvo “bachi” nel linguaggio (rarissimi sebbene possibili), il mantra del programmatore è: “Se qualcosa non ti funziona, è colpa tua”.

Nel documento Introduzione a SuperCollider (pagine 38-46)