sabato 29 dicembre 2007

Attributi per dichiarare le relazioni tra diverse rappresentazioni concrete di un tipo

Nel testo "Structure and Interpratations of Computer Programs", c'è una sezione che tratta di rappresentazioni multiple per dati astratti, con l'esempio delle due possibili rappresentazioni dei numeri complessi: forma polare e forma cartesiana, o rettangolare.

Vi è una corrispondenza biunivoca tra le due rappresentazioni, e la conversione da una rappresentazione all'altra è possibile sfruttando le seguenti relazioni:





Ho creato un progettino (in java) che investiga su questo argomento.

Una factory restituisce una rappresentazione concreta piuttosto che un'altra e lo fa reperendo i mapper che associano una rappresentazione ad un'altra.

Ogni costruttore di ogni implementazione, in modo dichiarativo, ovvero tramite attributi, mostra di essere mappabile in altri costruttori. Sa come trasformare la n-pla di parametri di inizializzazione nella equivalente n-pla usata da un altro costruttore e/o da un'altra implementazione.

Un complesso è definito come segue.

public interface ComplexNumber {
public double getReal();
public double getImg();
}

Il come costruirlo lo gestiremo nella factory.

Abbiamo le due seguenti implementazioni, Cartesiana e Polare:
public class ComplexNumberCartesian implements ComplexNumber {
protected double real;
protected double img;

@instanceConverter(instanceConverterMap = CartesianToPolarMapper.class)
public ComplexNumberCartesian(double real, double img) {
this.real=real;
this.img = img;
}

public double getReal() {
return real;
}

public double getImg() {
return img;
}

...


}





Polare:

public class ComplexNumberPolar implements ComplexNumber {
protected double magnitude;
protected Angle angle;

protected double real;
protected double img;

@instanceConverter (instanceConverterMap = PolarToCartesianMapper.class)
{
this.magnitude = magnitude;
this.angle = angle;

this.real = magnitude*Math.cos(angle.getValue());
this.img = magnitude*Math.sin(angle.getValue());
}

public double getReal() {
return real;
}

public double getImg() {
return magnitude*Math.sin(angle.getValue());
}


public boolean equals(Object object)
{
...
}

public int hashCode()
{

}
public String toString()
{
}
}

Nel costruttore abbiamo l'attributo @instanceConverter, che indica a sua volta la classe mapper, che associa a (real, img), l'equivalente coppia (ampiezza,angolo).

Ecco la definizione di questa annotation:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CONSTRUCTOR)
public @interface instanceConverter {
Class instanceConverterMap() default IdentityToObject.class;
}

Identity è il caso banale appunto dell'identità.

Noi sfruttiamo il mapper dalla forma cartesiana (o rettangolare) a quella polare:

public class CartesianToPolarMapper implements WrapperToObject{
public Wrapper getWrapperForClassConstructorWithParameters(
Class targetClass,
Class targetParamsTypes[],
Class[] originParamsTypes)
{

if (targetClass.equals(ComplexNumberPolar.class)) {
return new CartesianToPolar();
}
throw new RuntimeException("wrapper not defined for the target class");
}
}

In teoria la conversione specifica dipende anche dal tipo di costruttore della classe target invocato, e quindi si specifica anche il targetParamsType che è un array di classi (che consente di reperire il costruttore che accetta quelle classi come parametri).

Questo non viene considerato nel nostro caso perché abbiamo un solo costruttore per Polar.

Viene restituito un "wrapper", che è il seguente:

public class CartesianToPolar implements Wrapper {
public Object[] wrap(Object[] object) {
return new Object[] {Math.sqrt((Math.pow((Double) object[0],2.0))+Math.pow((Double) object[1],2.0)),new Angle(Math.atan2((Double) object[1], (Double) object[0]))};
}
}


Il wrapper inverso, cioè da Polare a Cartesiano, che può essere reperito tramite l'annotazione associata al costruttore della implementazione polare, è il seguente:


public class PolarToCartesian implements Wrapper{
public Object[] wrap(Object[] object) {
return new Object[]{(Double)object[0]*
Math.cos(((Angle)object[1]).getValue()),
(Double)object[0]*Math.sin(((Angle)object[1]).getValue())};
}
}


La factory sfrutta queste informazioni:


public class ComplexNumbersFactory {
....
public static ComplexNumber getComplexFromCartesianPar(double first ,double second)
{
if (CARTESIAN.equals(implementation))
{
return new ComplexNumberCartesian(first,second);
}

if (POLAR.equals(implementation))
{

ComplexNumber converted = (ComplexNumber) Utilities.getInstanceOfThisActualGivenConstructorOfOther(
ComplexNumberPolar.class,ComplexNumberCartesian.class,
new Object[]{first,second},
new Class[]{double.class,double.class},
new Class[]{double.class,Angle.class});
return converted;
}
throw new RuntimeException("unadmitted implementation mode "+implementation);
}

}

Questo è il codice che reperisce il tutto reperisce il codice di conversione, esegue la conversione, e restituisce l'equivalente istanza nell'oggetto target:


public static Object getInstanceOfThisActualGivenConstructorOfOther(
Class targetClass,
Class originClass,
Object[] instanceOriginCompatible,
Class[] instanceOriginClasses,
Class[] instTargetClass)
{
try {
Constructor constructor = originClass.getConstructor(instanceOriginClasses);
WrapperToObject wrapper = (WrapperToObject) constructor.getAnnotation(instanceConverter.class).instanceConverterMap().newInstance();
Object[] convertedPars = ((wrapper.getWrapperForClassConstructorWithParameters(targetClass, instTargetClass,instanceOriginClasses).wrap(instanceOriginCompatible)));
Constructor targetConstructor = targetClass.getConstructor(instTargetClass);
Object convertedObject = targetConstructor.newInstance(convertedPars);
return convertedObject;

} catch (Exception e) {
throw new RuntimeException(e);
}
}


Se la factory è settata in modo cartesiano, restituisce l'implementazione cartesiana senza nessuna conversione.

Se la factory è settata in modo polare, allora essa utilizza una funzione di conversine interrogando la classe ComplexNumberCartesian. Cioè chiede al suo costruttore, di fornire un wrapper in grado di eseguire il mapping tra parametri di istanza per il tipo concreto Cartesian, a parametri di istanza per il tipo concreto Polar.


Eventuali nuove estensioni non implicano cambiamenti al codice che ne faccia uso (salvo che eventualmente dover settare una proprietà), ma solo nella factory, purché queste nuove implementazioni rispettino il vincolo di dichiarare come mapparsi nelle implementazioni preesistenti (e viceversa).

La conversione, dovrebbe anche rispettare il principio che la conversione B->A applicata alla conversione A->B dovrebbe essere l'identità.

Verifichiamo con junit la creazione di due diverse implementazioni dello stesso numero, e ne testiamo l'uguaglianza:

     ComplexNumber first = new ComplexNumberCartesian(1.0,1.0);
ComplexNumbersFactory.setImplementation(ComplexNumbersFactory.POLAR);
ComplexNumber second= ComplexNumbersFactory.getComplexFromCartesianPar(1.0,1.0);

assertEquals(((ComplexNumberPolar)second).getAngle(),new Angle(Math.PI/4));
assertEquals(((ComplexNumberPolar)second).getMagnitude(),Math.sqrt(2.0));

assertEquals(first,second);


Il tipo concreto restituito dopo che abbiamo settato la factory in modo polar è appunto polare, e quindi il cast non da eccezione, ed inoltre sfruttiamo dei metodi aggiuntivi che solo il polar mette a disposizione, che sono getAngle e getMagnitude che restituiscono i valori che ci aspettiamo coerenti per il numero 1+i.


Riassumendo

Rispetto a diversi scopi un tipo concreto piuttosto che un altro può avere vantaggi di efficienza in casi particolari, ma bisogna nascondere la rappresentazione concreta per evitare che il programma chiamante dipenda da queste nuove implementazioni.

Usiamo attributi per rafforzare il legame che c'è tra classi imparentate tra loro.

"Extends" o "implements" garantisco che sintatticamente possono essere applicati a metodi deifiniti in termini della loro classe astratta (o interfaccia) ma niente di più.

Il dover mettere anche questa metainformazione dichiarativa può significare: ehi... se stai creando una nuova implementazione dovresti anche occuparti di dichiarare come fare a rendere possibile sostituire la tua implementazione al posto di quelle preesistenti, garantendo che tutto funzioni allo stesso modo di prima.


(nota: il codice versionato è stato rifattorizzato rispetto a quanto scritto in questo post, quindi potrebbe non corrispondere in nomi di classi packages).


Saluti e Buon Anno!

T.

mercoledì 5 dicembre 2007

Critico musicale Richard Benson



Richard Benson è chiamato a dare un giudizio sui cantanti che non sono stati accettati al festival di Sanremo.

primo video
secondo video
terzo video

Costituzione della libera repubblica di Užupis


Nel 1977 gli abitanti del quartiere di Užupis, a Vilinus (Lithuania) hanno proclamato la fondazione della loro Repubblica, con tanto di moneta, Presidente, esercito (formato, sembra, da 12 uomini).
Dopo aver letto la sua Costituzione, ho deciso di chiedere immediatamente la cittadinanza.

La Costituzione è formata dai seguenti 41 articoli:

1. Tutti hanno diritto di vivere vicino al fiume Vilnia e il fiume ha diritto di scorrere
2. Tutti hanno il diritto all'acqua calda, al riscaldamento d'inverno e a un tetto
3. Tutti hanno il diritto di morire ma non è un obbligo
4. Tutti hanno il diritto di fare errori
5. Tutti hanno il diritto di essere unici
6. Tutti hanno il diritto di amare
7. Tutti hanno il diritto di non essere amati
8. Tutti hanno il diritto di essere mediocri e sconosciuti
9. Tutti hanno il diritto di oziare
10. Tutti hanno diritto di amare un gatto e prendersi cura di lui
11. Tutti hanno il diritto di badare al cane fino a quando uno dei due muore
12. Il cane ha diritto di essere un cane
13. Il gatto non è obbligato ad amare il suo padrone, ma deve essere di aiuto nei momenti di necessità
14. A volte si ha il diritto di essere inconsapevoli dei propri doveri
15. Tutti hanno il diritto di avere dei dubbi, ma non è obbligatorio
16. Tutti hanno il diritto di essere felici
17. Tutti hanno il diritto di essere infelici
18. Tutti hanno il diritto di stare in silenzio
19. Tutti hanno il diritto di avere fede
20. Nessuno ha il diritto di usare violenza
21. Tutti hanno il diritto di apprezzare la propria scarsa importanza
22. Nessuno ha il diritto di avere un progetto per l'eternità
23. Tutti hanno il diritto di comprendere
24. Tutti hanno il diritto di non capire
25. Tutti hanno il diritto di appartenere a qualunque nazionalità
26. Tutti hanno il diritto di celebrare o non celebrare il proprio compleanno
27. Tutti devono ricordare il proprio nome
28. Tutti hanno il diritto di dividere ciò che posseggono
29. Nessuno può dividere ciò che non possiede
30. Tutti hanno il diritto di avere fratelli, sorelle e parenti
31. Tutti possono essere indipendenti
32. Tutti sono responsabili della propria libertà
33. Tutti devono poter piangere
34. Tutti hanno il diritto di essere fraintesi
35. Nessuno ha il diritto di dichiarare colpevole il prossimo
36. Tutti hanno il diritto all'individualità
37. Tutti hanno il diritto di non avere diritti
38. Tutti hanno il diritto di non avere paura
39. Non deludere
40. Non combattere
41. Non cedere



lunedì 26 novembre 2007

la qualità non è negoziabile

Nel suo libro relativo alla sua esperienza di Scrum e di Xp, Eric Kniberg spiega che il loro approccio nello sviluppo tra i vari principi adotta quello che la "qualità non è negoziabile".
Più precisamente si distingue tra qualità esterna e qualità interna.
La qualità esterna può essere rappresentata da una interfaccia utente scarsa, povera, anti-intuitiva, mentre la qualità interna può essere rappresentata da un codice robusto, ben testato, privo di bachi, manutenibile, etc...

La qualità esterna può essere considerata semplicemente parte dello scope, mentre la qualità interna dovrebbe essere un must non negoziabile.

La qualità percepibile è quella esterna, dunque si potrebbe essere tentati ad essere accomodanti a riguardo della qualità interna, ed accettare da parte del committente discorsi del tipo "rispetto il fatto che tu stimi che questa attività possa essere valutata in 6 story-points, ma sono sicuro che con un quick fix potresti trasformarla in una attività valutabile in soli 2 story-points, [tanto l'utente non se ne accorge]".

Questo discorso andrebbe respinto nei confronti del committente, anche se nella pratica questo è molto difficile, perché ciò che si vende è ciò che s vede.

Dunque è difficile dare il giusto valore alla qualità interna.

Se ne discute in un tread della lista "scrumdevelopment".

campagna anti-if

Campagna anti-"if". Ottima iniziativa!

sabato 20 ottobre 2007

httpxmlfixture

L'ultima cosa di cui mi sono occupato, per lavoro, è la creazione di una fixture per fitnesse, per fare acceptance testing e test driven development di applicazioni web.

Si tratta praticamente di una evoluzione di questa fixture che consente di fare alcune verifiche basate su xpath rispetto al contenuto di una pagina web, ovvero verificare che un certo nodo, individuato via espressione xpath, sulla pagina indicata da una certa url esista o meno, che abbia un certo valore, oppure che il numero di nodi che "matchano" l'espressione sia quello che ci si aspetta.

A questo ho aggiunto le seguenti caratteristiche:

- poter accedere a pagine che richiedono autenticazione (basic o, in alcuni casi, form)
- poter lavorare anche su pagine che non sono ben formate come xml, usando jTidy come preprocessor
- gestire i cookies così potendo gestire anche la sessione di navigazione (se basata su cookie)
- gestire eventuale submission di parametri via post o get.
- poter generare automaticamente un test che va a buon fine rispetto ad un esempio statico.

In teoria dovrebbe essere sufficiente per poter fare dei chiari test di accettazione per applicazioni web, almeno se si tratta di testare solamente cose "server side" (niente javascript/ajax quindi).

Per esempio: il customer si dice soddisfatto che l'applicazione renderizzerà una pagina in modo conforme ad un certo esempio fornito come pagina statica. A quel punto la pagina statica viene "processata" da xml2xpath, e viene creato il relativo test che ha successo quando la pagina viene "deployata".

Supponiamo che nella pagina vi sia una tabella, che dovrà poi contenere dati dinamici.
Il customer vorrà magari che "in quella tabella poi dovranno essere presenti i dati che verranno inseriti in una certa form".
Allora si modifica subito l'espressione xpath che verifica i dati in tabella, e si fa in modo che il test sia esteso in modo da incluere anche l'azione di "inserire un certo dato, e poi andare nella pagina dell'elenco ed aspettarsi di trovare quel dato".

La cosa descritta adesso è più o meno una user story e l'idea è di scrivere un test che la "descrive", e ne verifica anche la corretta implementazione.

Un po' più in dettaglio se la pagina statica pensata all'inizio è del tipo:


<html>
<head/>
<body>
<table>
<tr>
<td>entry1</td>
</tr>
<tr>
<td>entry2</td>
</tr>
<tr>
<td>entry3</td>
</tr>
</table>
</body>
</html>



allora lo stylesheet xml2xpath genera il seguente pezzo di tabella di test:

| Value | /html/body/table/tr/td | entry1 |
| Value | /html/body/table/tr[2]/td | entry2 |
| Value | /html/body/table/tr[3]/td | entry3 |

a questo punto, se si progetta che l'azione di inserimento di questi dati dovrà essere data da una certa jsp che si chiamerà submitaction.jsp, con un parametro entryname, allora, prendendo un po' spunto dal test precedente, l'intera storia "sottometti il dato entryX e poi verifica che
visualizzando la tabella, esso sia presente", è un po' come segue":

...
|newUrlPost|http://myserver/submitaction.jsp|
|NameValuePair|entryname|entryX|
|newUrlGet|http://myserver/visualize.jsp |
|AValue|/html/body/table/tr[*]/td | entryX |

Ovviamente poi il test resta lì utile anche a verificare non solo quando l'implementazioe è corretta, ma anche che continui a rimanere corretta.

cosa e' (era) learph

Cercando in rete e' ancora possibile trovare, in qualche pagina che raccoglie informazioni sui sistemi di sintesi e riconoscimento vocale, un riferimento a "learph. Trainable text-to-phonemes software by Antonio Lucca" (che sarei io), e che risale al lontano '93.

(Il nome fa veramente schifo, avevo messo insieme la parola "learn" e "phonemes".
Considerando poi che l'inglese è una lingua che usa spesso parole onomatopeiche, lasciamo stare.)

Non è possibile scaricarlo perché il link non è più valido. Potrei ripubblicarlo ma non so se ne valga la pena.

Questo programma non sarebbe altro che una "demo", in C, utilizzabile in riga di comando, che "impara", a partire da un training set di esempi di parole inglesi associate alla loro corrispondente rappresentazione fonetica, le regole di traduzione da parola a fonemi, cioè sostanzialmente le regole del modo in cui si pronunciano.

Il training set era stato ottenuto banalmente prendendo un vocabolario ed applicando ad ogni sua parola un preesistente algoritmo di traduzione testo-fonemi.

Quindi in sostanza il progetto era di creare un sistema che, sulla base di esempi, impara ad eseguire un compito per il quale già esiste un algoritmo! :-)

(La descrizione è qui semplificata perché esiste la possibilita che un gruppo di lettere sia associato a più fonemi e viceversa, che una lettera sia muta e così via. Per gestire questo avevo modificato il training set un po' a mano aggiungendo queste informazioni, sulla base di decisioni sostanzialmente arbitrarie ma intuitivamente valide. Inoltre, per semplificare, avevo eliminato i simboli relativi all'intonazione/accento/stress)

Il progetto mi era venuto in mente per le seguenti motivazioni:
. ero un orgoglioso possessore del Commodore Amiga, che era uno dei pochi computer dotati di software per la sintesi vocale
. seguivo il corso di cibernetica e teoria dell'informazione, che trattava di temi come il computational learning, e ho scelto questo progetto come tesina
. avevo sentito dire che era stato implementato un sistema basato su reti neurali, chiamato NetTalk, che era stato concepito per quello scopo

Avevo dato un'occhiata al codice relativo ad algoritmi ad hoc per questo compito, e si tratta di algoritmi abbastanza "ingarbugliati", perché sostanzialmente non c'è una precisa regolarità nella pronuncia.

Tuttavia una metaregola generale banalmente c'è, su cui anche il "nettalk" è basato: il fatto che ogni lettera si pronuncia in modo diverso in funzione del suo contesto, cioè delle lettere vicine.

L'algoritmo che avevo implementato è basato su questo principio ed in particolare si comporta come segue:
memorizza ogni associazione singola lettera->fonema riscontrata negli esempi, ed assumi in prima istanza che questa associazione sia una regola generale, a meno che tu non trovi un esempio che contraddice questa regola
Quando trovi un esempio che contraddice questa regola, allora elimina la regola, e crea nuove regole in cui tieni conto delle informazioni di contesto del primo e del secondo caso, ovvero regole che associano non la lettera stessa al fonema, ma la lettera preceduta da una certa lettera e seguita da una certa altra lettera.

Naturalmente anche queste regole potrebbero essere in contraddizione tra loro, cioè no risolvere completamente l'ambiguità. In quel caso si espande l'ipotesi e si include nel contesto anche la lettera immediatamente precedente e successiva al quella "tripla".

E così via fino a che le regole risultano tutte consistenti rispetto al training set.

Una volta memorizzate le regole, l'algoritmo di pronuncia che associa ad una parola la sua rappresentazione fonetica non fa altro che parsare lettera per lettera la parola in ingresso, e fa un lookup sulle regole memorizzate, che dicono sostanzialmente come andrebbe pronunciata quella lettera in quel contesto.

Naturalmente questo significa che per le parole appartenenti al training set il sistema funziona perfettamente, mentre se ci sono parole non appartenenti al training set potrebbe esserci ancora qualche ambiguità. In quel caso non ricordo bene che scelta feci. Se non erro c'era un mix di euristica unita ad una scelta casuale, tra quelle possibili.

Comunque funzionava abbastanza bene, nel senso che il principio era sufficentemente semplice e generale, funzionava al 100% sul training set (mentre è noto che con le reti neurali questo di solito non avviene), la struttura dati contenente le regole era relativamente piccola, una sorta di albero di decisione, e consentiva una buona generalizzazione.

Questo progetto fu fatto nel lontano '93, ma non ricordo quando lo feci aggiungere al repository comp.speech, ne' so quante persone lo abbiano scaricato, visionato.

mercoledì 17 ottobre 2007

annotations speciali associate a metodi "mutators" e "observers" per mostrare una prova empirica della sostituibilita' tra oggetti

Ho creato un nuovo progetto su esperimenti relativi alla nozione object oriente di sottoclasse, sostituibilità usando le annotazioni di java 5.

http://testlspjava5.googlecode.com/

I metodi possono distinguersi in diversi tipi, tra cui, mutators ed observers.
Grosso modo: i mutators modificano lo stato dell'oggetto, gli observer ne ispezionano lo stato.
Sono associabili, tramite annotazioni, per ogni mutator, degli "instance genertor" che generano una sequenza ripetibile di n-pla di parametri applicabili a tale metodo mutator.

Viene così simulata, sotto il controllo di junit, una computazione "casuale" ma ripetibile, che coinvolge la classe e che agisce sui mutators, creando dunque una sequenza di stati per un generico oggetto della classe.
Viene ripetutta una seconda volta la stessa computazione usando la sottocalsse anziché la classe.

La prova empirica della sostituibilità sta nel fatto che la sequenza di stati della prima computazione (che riguarda la classe) è equivalente alla seconda sequenza di stati della computazione (che riguarda la sottoclasse).

L'equivalenza è definita in termini di observers, ovvero, ricorsivamente, di equivalenza di stato di tutti i valori/oggetti restitui dagli observers.

Informazioni personali

La mia foto
I have been coding from the old C64 times. Studied Computer Sciences at Milan University. I also worked there in technical operations. Many years of experiences in coding Java and C#, desktop and web applications, with practices like unit testing. I used to play with 3d graphics in architecture recently with Blender 3d. Now I look for support related to some projects I am working on, oriented in automation in tourism related services, using functional programming framework, specifically F# and Suave.IO. email
tonyx1 (at) gmail.com github https://github.com/tonyx