Système de numération binaire représenté par l'agrégation et la comparaison d'objets.

Dans un espace initialement vide, ajoutez des entités en apliquant la règle suivante:
Si deux entités sont similaires, agrégez-les en une nouvelle entité, et réitérez si nécessaire, tant que vous trouvez des agrégats similaires.
{} le vide initial.
{o} je viens de placer une première entité.
{(o o)} j'ai placé une seconde entité, et, comme elles étaient identiques, je les ai regroupées.
ajoutons une entité supplémentaire : {o, (o o)} Ici, pas de regroupement possible.
pour la quatrième entité par contre, on passe de {o, o, (o o)} à {(o o), (o o)} par un premier regroupement, et on arrive à {((o o) (o o))} par un second regroupement des deux agrégats similaires.
et cætera, et cætera...
Pour passer du nombre d'entités unitaires à la représentation d'un entier positif en base deux, il suffit d'indiquer le nombre d'agrégats de chaque sorte. Par exemple 3 <=> {(o o), o} <=> 011, c'est-à-dire 0 groupe de 4, 1 groupe de 2 et 1 entité unité.
5 <=> {((o o) (o o)), o} <=> 101, c'est-à-dire 1 groupe de 4, 0 groupe de 2 et 1 entité unité.

Implémentation en Smalltalk

Nous manipulons trois types d'objets : L'espace noté par des accolades, contenant un ensemble d'entités de deux types possibles, l'unité, noté o et l'agrégat noté par les parenthèses.
Créons les trois classes correspondantes :

Object subclass: #ZEntier
 	instanceVariableNames: 'entites '
 	classVariableNames: ''
 	poolDictionaries: ''
 	category: 'VL-BaseDeux'
Object subclass: #ZUnite
  	instanceVariableNames: ''
  	classVariableNames: ''
  	poolDictionaries: ''
  	category: 'VL-BaseDeux'
Object subclass: #ZPaire
  	instanceVariableNames: 'moitie '
  	classVariableNames: ''
  	poolDictionaries: ''
  	category: 'VL-BaseDeux'

ZEntier représente l'espace, il possède une variable d'instance entites qui contiendra un ensemble d'entités ZUnite ou ZPaire.
ZUnite représente l'unité o.
Zpaire représente tous les agrégats. Comme les agrégats formés sont toujours l'agrégation de deux entités similaires, une seule variable d'instance suffit pour mémoriser la moitié de l'information. La variable d'instance moitie pointera soit vers une ZUnite soit vers une autre instance de ZPaire.
Il n'y a pas de super-classe commune à ZPaire et ZUnite car ces deux types d'entités, malgrés le fait qu'ils répondent en partie aux mêmes messages, n'ont rien en commun. Mon premier reflex avait été de créer une classe ZEntite super-classe de ZPaire et de ZUnite, mais, à la fin du développement, j'ai remarqué que cette classe ne portait aucune définition, ni de méthode, ni de variable d'instance. Donc je l'ai supprimée.
Première définition, nous voulons que, lors de la création d'un espace, un ensemble vide d'entité soit créé.

ZEntier class [instance creation]
new
	^ super new initialize
ZEntier [initialize-release]
initialize
  	entites := Set new. 

Ensuite nous pouvons créer le zéro.

ZEntier class [instance creation]
zero
  	^ self new

Pour faire 'un', il suffit de placer une instance de ZUnite dans l'ensemble. Mais pour cela, il faut tout de même respecter les règles d'ajout d'entité dans l'espace:

ZEntier class [instance creation]
unity
  	^ self zero ajoute: ZUnite new

Définissons donc cette règle.

ZEntier [accessing]
Code initial
ajoute: uneEntite
  	| id |
  	(id := entites detect: [:y | y equal: uneEntite ]
  		ifNone: [ entites add: uneEntite. nil ]) isNil ifFalse: [
  			entites remove: id.
  			self ajoute: (ZPaire double: id) ]

Paraphrase: Pour ajouter une entité, chercher (detect:) dans l'ensemble des entités de l'espace, la première entité y similaire (equal:) à cette entité. s'il y en a une, la nommer id, s'il n'y en a pas (ifNone:), ajouter l'entité à l'ensemble et mettre nil dans id (programme terminé). Si id n'est pas nil (alors id contient une entité similaire à la première, et appartenant à l'ensemble), retirer id de l'ensemble, et ajouter à l'espace (en respectant les même règles: appel récursif), un nouvel agrégat formé par le 'double' de id. id et uneEntite étant similaires, on ne mémorise dans la ZPaire que la moitié de cette paire.
Plus globalement: Pour ajouter une entité, si cette entité n'existe pas on l'ajoute à la liste, et si elle y était déjà, on la regroupe avec l'entité déja présente. puis, on vérifie que la nouvelle entité ainsi constituée n'était pas déjà présente.

Les nouveaux messages à définir sont: equal: et double:.
equal: est sensé ramener vrai si les deux entités sont similaires et double: est un message d'instanciation pour la classe ZPaire.

ZPaire class [instance creation]
double: uneEntite
  	^self new moitie: uneEntite


ZPaire [accessing]
moitie: uneEntite
  	moitie := uneEntite

Là j'ai visiblement un problème de vocalulaire, quand j'ai écris (ZPaire double: id) je voulais dire à la classe ZPaire de redoubler la valeur id passée en argument. Et pour le faire, ^self new moitie: uneEntite, je crée une instance de ZPaire à laquelle je dis que sa moitié est la valeur donnée en paramètre. En bref: pour créer (o o), on écrit (ZPaire double: ZUnite new), dont la moitié est la ZUnite o.
 
Définissons maintenant l'égalité (ou la similitude) des entités. C'est en fait le point crucial, ce qui permet lors de l'ajout, de déclencher le regroupement des entités reconnues commes similaires.
Le principe utilisé ci-dessous est le suivant: quand on veut connaitre le type (la classe) des deux objets en présence dans un message binaire (ou à un mot clef), il suffit de renvoyer un message au paramètre (en inversant les rôles entre le récepteur et l'argument).

ZPaire [comparaison]
equal: uneEntite
  	^ uneEntite equalPaire: self

Nous connaisons la classe du récepteur, c'est ZPaire, par contre nous ne connaissons pas la classe du paramètre. On renvoie un message equalPaire: au paramètre.

ZUnite [comparaison]
equalPaire: uneEntite
	^false

Si c'est une ZUnite qui reçoit le message, comme uneEntite est une paire, on est sûr que les entités ne sont pas similaires.

ZPaire [comparaison]
equalPaire: uneEntite
	^ uneEntite moitie equal: moitie

Cette fois, on sait que self est une ZPaire et que uneEntite est une ZPaire également. Si leurs moities sont égales, elles le sont aussi.
Il faudra définir le message moitie pour accéder à la moitié de la paire passée en argument, à moins de redéfinir le message equal: pour les ZPaires en ne passant que la moitié de la paire réceptrice en argument du message equalPaire: mais ça complique la lecture.

ZPaire [accessing]
moitie
  	^ moitie

Passons au cas de l'unité, plus simple puisqu'elle ne peut être similaire qu'à une autre unité. En trois messages:

ZUnite [comparaison]
equal: uneEntite
  	^ uneEntite equalUnite


ZUnite [comparaison]
equalUnite
  	^true


 ZPaire [comparaison]
equalUnite
  	^false

Voilà, nous pouvons ajouter un exemple:

ZEntier [examples]
example01
  	^ self unity ajoute: (ZUnite new)

Résultat : {(o o)}
Mais pour que le résultat apparaisse de la sorte, il faut définir les méthodes d'impression des différentes classes:

ZEntier [printing]
printOn: aStream
  	aStream nextPut: ${.
  	entites do: [:x | x printOn: aStream ]
  		 separatedBy: [ aStream nextPutAll: ', ' ].
  	aStream nextPut: $}.
 
ZPaire [printing]
printOn: aStream
  	aStream nextPut: $(.
  	moitie printOn: aStream.
  	aStream nextPut: $ .
  	moitie printOn: aStream.
  	aStream nextPut: $).
 
ZUnite [printing]
printOn: aStream
  	aStream nextPutAll: 'o'

Maintenant nous pouvons faire un peu d'arithmétique:

ZEntier [arithmetique]
+= unZEntier
	"Incremente le recepteur de unZEntier"
	unZEntier entites do: [:x | self ajoute: x ]

Cette première méthode modifie le récepteur en lui ajoutant toutes les entités contenues dans l'argument.
Pour accéder aux entités de l'argument, il est nécessaire de définir le message d'accès:
ZEntier [accessing]
entites
	^ entites

La méthode suivante crée une nouvelle instance et ne modifie pas le récepteur.

ZEntier [arithmetique]
+  unZEntier
	| somme |
	somme := self class zero.
	somme += self.
	somme += unZEntier.
	^ somme

Et enfin, la multiplication, vue comme la répétition d'une addition.

ZEntier [arithmetique]
* unZEntier
	| product action |
	product := self class zero.
	action := [ product += unZEntier ].
	self timesRepeat: action.
	^product

Mais, le message timesRepeat: n'est pas encore défini. Pour ce faire, définissons le message dans la classe ZEntier. On peut parcourir les entités récursivement, et pour chaque ZUnite rencontrée exécuter le bloc.
En trois méthodes:

ZEntier [enumerating]
timesRepeat: aBloc
	"attention: il faut forcer la non optimisation
	  a l'appel également 
		ne pas faire: 
		unZEntier timesRepeat:  [ tagada ].
		mais faire:
		action := [ tagada ]. unZEntier timesRepeat: action"

	entites do: [:x | x timesRepeat: aBloc ]

Notez que le commentaire indique la façon particulière d'appeler cette méthode, ceci est du au fait que le compilateur optimise habituellement les méthodes utilisant le message timesRepeat:. Voir cette page.
Méthode timesRepeat: pour les entités:
ZPaire [enumerating]
timesRepeat: aBloc
	moitie timesRepeat: aBloc.
	moitie timesRepeat: aBloc


ZUnite [enumerating]
timesRepeat: aBloc
	aBloc value

Pour chaque ZUnite recontrée, on exécute le bloc, et les ZPaires ne font que retransmettre le message deux fois à leur moitié.

v i @ a i . u n i v - p a r i s 8 . f r : Recevez le code en fichier .st sans avoir à faire une trentaine de copier/coller.