Qu'est-ce qu'une pile et pourquoi l'utiliser ?
Le principe d'une PILE est LIFO (Last In First Out), comprenez que le dernier élément de la pile sera le premier à être lu. Un exemple grossier d'utilisation serait une gestion de
CTRL-Z à l'interieur d'une application.
On empile les actions de l'ulisateurs et on dépile à chaque
CTRL-Z. Dans ce genre de structure, on ne peut donc accéder au'au dernier éléments. Quoi que l'on fasse, où que l'on soit, nous aurons toujours accès au dernier éléments et pas à un autre. C'est tout l'avantage de créer nos propres structures de données, on peut gérer les accès comme nous le souhaitons.
Comment mettre en place cette structure ?
Voyant déjà dans le futur Laughing,
il y a 2 sortes piles à disposition :
- BasicStack (pile d'objet indépendant à tous sens)
- TypedStack (pile d'objet indépendant mais de même type)
Afin de conserver une parfaite intégriter je vais donc commencer par vous liver une interface
Stack.as qui va permettre de faire le lien entre ces 2 classes, et permettre en même temps, l'utilisation du typage pour gérer nos piles.
Interface Stack.as :
ActionScript Code:
interface Stack
{
/**
* Ajouter une valeur dans la pile<br />
* @param p_value Une donnée de type quelconques (pas de typage fort)
*/
public function push(p_value) : Void;
/**
* On récupère la dernière valeur et on la supprimer de la pile<br />
* @return la dernière donnée (pas de typage fort)
*/
public function pop(Void);
/**
* On récupère la dernière valeur<br />
* @return la dernière donnée (pas de typage fort)
*/
public function peek(Void);
/**
* La pile est-elle vide<br />
* @return Boolean true si la pile est vide.
*/
public function isEmpty(Void) : Boolean;
/**
* Retourne le nombre d'éléments de la pile<br />
* @return Number Nombre d''éléments
*/
public function getLength(Void) : Number;
/**
* Tranformation pile -> tableau<br />
* @return Array Tableau comportant les données de la pile
*/
public function toArray(Void) : Array;
/**
* Affichage du contenu<br />
*/
public function toString(Void) : String;
}
Maintenant le code de la 1ère pile, c'est à dire
BasicStack :
ActionScript Code:
class BasicStack implements Stack
{
private var __aDatas : Array;
public function BasicStack(p_aData : Array)
{
__aDatas = (p_aData) ? p_aData.concat() : __aDatas = new Array()
}
public function push(p_value) : Void
{
__aDatas.push(p_value);
}
public function pop(Void)
{
if (isEmpty()) {
throw new Error("You tried to pop an element from an empty Stack.");
}
return __aDatas.pop();
}
public function peek(Void)
{
if (isEmpty()) {
throw new Error("You tried to peek an element from an empty Stack.");
}
return __aDatas[__aDatas.length-1];
}
public function isEmpty(Void) : Boolean
{
return (__aDatas.length < 1);
}
public function getLength(Void) : Number
{
return __aDatas.length;
}
public function toArray(Void) : Array
{
var aTemp : Array = __aDatas.concat();
aTemp.reverse();
return aTemp;
}
public function toString(Void) : String
{
return '[BasicStack] \n\t\t' + toArray().toString();
}
}
Maintenant quelques exemples d'utlisation :
ActionScript Code:
var myPile : Stack = new BasicStack();
myPile.push("communauté");
myPile.push("adddvance");
myPile.push(28);
trace( myPile.pop() ) // Affiche 28
trace( myPile.pop() ) // Affiche adddvance
trace( myPile.getLength() ) // Affiche 1
trace( myPile.peek() ) // Affiche communauté
trace( myPile.getLength() ) // Affiche 1
Imaginons maintenant que l'on veulle vérifier que les données que nous voulons ajouter soient compatible, entendez par là, de même type.
En effet, il peut être très pratique de vérifer le typage des données que l'on gère.
Si nous voulons travailler uniquement sur des chaines de caractères par exemple, nous devons vérifer que chaque données empiler dans notre pile est bien de type "chaine de caractère". Pour cela nous allons créer une nouvelle classe Typedstack. Cette class va hériter de notre 1ere class
BasicStack tout en implémentant notre interface Stack. Code de la classe
TypedStack :
ActionScript Code:
classTypedStack extends BasicStack implements Stack
{
private var __fType : Function;
public function TypedStack(p_fValue : Function)
{
__fType = p_fValue;
if(!p_fValue)
throw new Error("You must define a type for TypedStack")
if (arguments.length > 1) {
arguments.shift();
super(arguments);
}
}
public function push(p_value) : Void
{
_validateData(p_value);
super.push(p_value);
}
public function toString(Void) : String
{
return '[TypedStack] \n\t\t' + __aDatas.toString();
}
private function _validateData(p_value) : Void
{
if(!com.asdk.utils.ClassUtil.typeOf(p_value, __fType))
throw new Error("Invalid type " + p_value)
}
}
Vous remarquerez que le grand changement ce fait au niveau de la méthode push qui vérifie maintenant le type de donnée et empile celle ci uniquement si le type est correcte.
Pour vérifer le type des données envoyées à la pile, j'ai réalisé une simple méthode statique de test. Cette méthode sera certainement à revoir pour la gestion de types plus complexe, je ne la considère donc pas comme définitive à l'heure actuelle.
Classe ClassUtil :
ActionScript Code:
class com.asdk.utils.ClassUtil
{
static public function typeOf(instance, type) : Boolean
{
return instance.__constructor__ == type;
}
}
Exemple d'utilisation :
ActionScript Code:
var myPile : Stack = new TypedStack(String, "toto");
myPile.push("Romain");
trace(myPile) //Affiche toto, romain
myPile.push(28); //Exception de la classe TypedStack.Arret
Encore rien de bien compliqué, mais ce genre de structure permet souvent d'éliminer des erreurs de typage à l'intérieur de nos projets.