[!java|scala]Tableaux[/!][!python]Listes[/!] et tricot

L'objectif de cet exercice est de reproduire le motif de la première colonne en le décalant d'une case (voir l'onglet «Objectif» pour plus de détails). La grande différence entre cet exercice et les précédents sur les motifs, c'est qu'il faut maintenant lire (sur la première colonne) le motif souhaité, puis le reproduire ensuite. Il est impossible de faire autrement car votre programme sera exécuté par trois buggles dans trois mondes différents, chacune ayant un motif propre à reproduire.

Une possibilité est de lire la prochaine case, puis d'aller la recopier en position, avant de revenir lire la case suivante, etc. Mais comme vous n'avez pas le droit d'utiliser les méthodes permettant de téléporter la buggle à une case particulière (setPos() et autres), cette façon de faire va être très pénible à mettre en place.

Le plus simple est de stocker l'enchainement de couleurs constituant le motif dans [!java|scala]un tableau[/!][!python]une liste[/!]. Mais avant de pouvoir faire cela, nous devons en apprendre un peu plus sur les [!java|scala]tableau[/!][!python]listes[/!].

[!java|scala]Les tableaux[/!][!python]Les listes[/!]

[!java|scala]Un tableau[/!][!python]Une liste[/!] est une séquence ordonnée de variables qui marchent ensemble. C'est un peu similaire à une commode dont les différents tiroirs peuvent stocker des valeurs différentes. Chaque variable de la séquence est identifiée par sa position et peut stocker une valeur spécifique. [!java|scala]Toutes les cellules d'un tableau doivent stocker des valeurs du même type de données parce que les tableaux sont homogènes en [!thelang]. Il est cependant possible de contourner cette restriction en utilisant le type de données [!java]Object[/!][!scala]Any[/!] qui peut contenir [!java]presque[/!] tous les autres type de données[!scala] («any» veut dire «n'importe» en anglais)[/!]. [!java]Les types primitifs comme ceux que nous avons utilisé jusqu'à présent (int, boolean, double, char, etc) ne peuvent pas être stockés dans une variable Object, mais leurs variantes objectifiées (Integer, Boolean, Double, Char, Boolean, etc) peuvent l'être.[/!] Il est cependant raisonnable de rendre ses tableaux aussi spécifiques que possible. Si vous avez l'intention de stocker des entiers, faites en un tableau de [!java]integer[/!][!scala]Int[/!], pas de [!java]Object[/!][!scala]Any[/!].[/!] [!python]Les listes peuvent contenir des données de différents types, en mélangeant par exemple quelques valeurs entières dans certaines cellules avec des entiers dans d'autres cellules.[/!]

T est le nom [!python]de la liste[/!][!scala|java]du tableau[/!], [!java|python]T[0][/!][!scala]T(0)[/!] est le nom de la première case, [!java|python]T[1][/!][!scala]T(1)[/!] de la deuxième case, [!java|python]T[2][/!][!scala]T(2)[/!] de la troisième case, etc... Et oui, la première case est [!java|python]T[0][/!][!scala]T(0)[/!] et la dernière case [!python]d'une liste[/!][!scala|java]d'un tableau[/!] de taille N est [!java|python]T[N-1][/!][!scala]T(N-1)[/!]. Cela peut sembler étrange de commencer à compter à partir de 0 et non de 1, mais c'est ainsi (et cela s'explique par des raisons historiques obscures).

Usage de base

On peut utiliser une variable entière i pour accéder avec [!java|python]T[i][/!][!scala]T(i)[/!] aux cases. Quand i vaut 0 alors [!java|python]T[i][/!][!scala]T(i)[/!] dénote la case [!java|python]T[0][/!][!scala]T(0)[/!]; lorsque i vaut 10, [!java|python]T[i][/!][!scala]T(i)[/!] dénote [!java|python]T[10][/!][!scala]T(10)[/!]. On dit alors que i est un indice dans [!java|scala]le tableau[/!][!python]la liste[/!] T. [!java|python]T[i][/!][!scala]T(i)[/!] peut être utilisé comme n'importe quelle variable. On peut lui affecter une nouvelle valeur:

[!java|python]T[i][/!][!scala]T(i)[/!] = 78[!java];[/!]

On peut réutiliser et tester cette valeur :

x = [!java|python]T[i][/!][!scala]T(i)[/!][!java];[/!]

On peut tester cette valeur :

if ([!java|python]T[i][/!][!scala]T(i)[/!] > 0) [!scala|java]{[/!][!python]:[/!]
    [!java|scala]//[/!][!python]#[/!] instructions...
[!java|scala]}[/!]

Il est également très simple de parcourir [!java|scala]tout le tableau[/!][!python]toute la liste[/!], par exemple pour initialiser chaque cellule.

[!java]for (int i = 0; i<T.length; i++) {[/!][!python]for i in range(len(T)):[/!][!scala]for (i <- 0 to T.length-1) {[/!]
   [!java|python]T[i][/!][!scala]T(i)[/!] = 3[!java];[/!]
[!java|scala]}[/!]

[!java|scala]La notation T.length permet d'accéder à la taille («length» en anglais) du tableau T,[/!] [!python]La fonction len() renvoie la longueur de la liste T,[/!] ce qui permet de construire facilement la boucle. [!python]En fait, la fonction len() est bien plus générique et peut être utilisée pour calculer la taille de nombreux objets. Appliquée à une chaîne de caractères par exemple, elle retourne le nombre de caractères composant cette chaîne.[/!] [!scala]N'oubliez pas de commencer à 0 pour terminer à T.length-1 au lieu de 1 to T.length![/!]

Si vous souhaitez simplement parcourir les valeurs de T sans avoir besoin de l'index de chaque valeur, vous pouvez écrire simplement :

[!java]for (int i: T) {[/!][!scala]for (i <- T) {[/!][!python]for i in T:[/!]
  action()[!java];[/!]
[!java|scala]}[/!]

[!java]Cette construction s'appelle une boucle for étendue en Java. La variable i prend successivement toutes les valeurs de l'ensemble placé à droite des deux-points (:).[/!] [!python|scala]Cette écriture est finalement très semblable à la précédente. Simplement, [!python]range(n)[/!][!scala]i to j[/!] retourne un ensemble d'entiers sur lequel la boucle for itère. En fait, [!thelang] offre d'autres moyens très élégants de traverser des [!python]listes[/!][!scala]tableaux[/!] et d'autres collections de données. Mais cela devrait être le sujet d'exercices spécifiques (qui restent à écrire dans PLM).[/!]

Declarer [!python]une liste[/!][!java|scala]un tableau[/!]

[!python]

Si vous connaissez à l'avance le contenu de votre liste, vous pouvez affecter ces valeurs directement. Placez-les simplement entre crochets et séparées par des virgules comme ceci :

L = [1, 3, 5, 7, 9] 
# L est maintenant une liste de 5 valeurs, toutes des entiers

Dans le cas contraire, le plus simple est de créer une liste vide puis d'ajouter chaque valeur séparément à la liste :

L2 = [] 
# L2 est maintenant une liste vide
L2.append(1)
L2.append(3)
L2.append(5)
L2.append(7)
L2.append(9) 
# Son contenu est maintenant le même que celui de L ci-dessus
[/!] [!java|scala]

Pour créer une variable nommée T pouvant contenir un tableau d'entiers, on écrira :

[!java]int[] T;[/!][!scala]var T:Array[Int][/!]

[!java]int indique que les éléments du tableau sont de type entier; [] indique que nous parlons d'un tableau tandis que T est le nom de la variable. Pour des raisons historiques, cela peut également être écrit sous la forme int T[] (avec [] à droite du nom de la variable), mais cette forme est moins lisible et devrait probablement être évitée.[/!] [!scala]La notation [Int] spécialise le type Array («tableau» en anglais), qui est générique. Cela spécifie que chaque case du tableau est un entier. Le type d'un tableau de booleens s'écrirait simplement Array[Boolean].[/!]

Allocation d'un tableau

Déclarer un tableau T nous réserve juste le nom T pour l'utiliser plus tard, mais pas la place en mémoire pour stocker les cases. Le tableau n'est pas initialisé : il n'a pas de valeur. Que voudrait dire T[4] si nous n'avons pas encore dit que T est un tableau de 5 éléments ?

Avant tout, il faut donc lui affecter une valeur à T:

[!java]T = new int[10];[/!][!scala]var T = new Array[Int](10)[/!]

new indique qu'il faut créer quelque chose, et [!java]int[10][/!][!scala]Array[Int](10)[/!] indique qu'il s'agit d'un tableau de 10 valeur entières. En réponse, un tableau d'entiers de longueur 10 est crée en mémoire, et la variable T référence ce tableau.

La taille d'un tableau est fixée et ne peut plus être changée après la création du tableau. Pour connaître la taille d'un tableau T, on peut consulter la variable T.length.

Lors de l'allocation, vous pouvez spécifier la taille à utiliser avec une variable: [!java]int[] T = new int[i];[/!][!scala]var T = new Array[Int](i);[/!] Dans ce cas, la taille du tableau est fixée à la valeur de i quand new a été appelé. La taille du tableau ne peut toujours pas être modifiée. Même si la valeur de i est modifiée ensuite, la taille reste la même. [!java]Enfin, il est interdit d'écrire quelque chose comme int T[10]; pour déclarer la variable. Il faut absolument utiliser new pour l'allouer, comme dans int[] T = new int[10]; [/!]

Déclaration et initialisation

Si vous connaissez le contenu de votre tableau à l'avance, vous pouvez le déclarer, l'allouer et l'initialiser en un coup:

[!java]int[] T = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };[/!][!scala]var T = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)[/!]

Pour connaître la taille du tableau à allouer, le compilateur compte les valeurs données. Ce code est équivalent à :

[!java]int[] T = new int[10];
T[0] = 1;
T[1] = 2;
...
T[9] = 10;[/!][!scala]var T = new Array[Int](10);
T(0) = 1
T(1) = 2
...
T(9) = 10[/!]

C'est aussi équivalent au code :

[!java]int[] T = new int[10];
for (int i=0; i<T.length; i++) {
  T[i] = i+1;
}[/!][!scala]var T = new Array[Int](10);
for (i <- 0 to T.length-1) {
  T(i) = i+1
}[/!]
[/!]

Les [!python]listes[/!][!scala|java]tableaux[/!] et les paramètres de méthodes

On peut tout à fait passer [!java|scala]un tableau[/!][!python]une liste[/!] en paramètre d'une méthode. La méthode peut alors l'utiliser comme si la variable avait été définie localement:

[!java]boolean a42Premier(int[] tableau) {
    return tableau[0] == 42;
}[/!][!python]def a42Premier(liste):
  return liste[0] == 42[/!][!scala]def a42Premier(tableau:Array[Int]):Boolean = {
  return tableau(0) == 42
}[/!]

Coté appelant, c'est aussi simple :

[!java]int[] tab = new int[10];[/!][!scala]var tab = new Array[Int] (10)[/!][!python]tab = [1, 3, 5, 7, 9][/!]
[!java|scala]// Initialisation des valeurs omise
[/!]if (a42Premier(tab))[!java|scala] {[/!][!python]:[/!]
   [!java|scala]//[/!][!python]#[/!] faire des choses
[!java|scala]}[/!]
[!java]

Si vous voulez allouer et initialiser le tableau au vol lors du passage de paramètre, c'est un peu plus compliqué car il faut dire au compilateur le type du paramètre que vous construisez. Il faut alors utiliser la construction suivante, même si elle n'est pas très belle.

if (has42First(   new int[] {1, 3, 5, 7, 9}   ) {
   // faire des choses
}
[/!]

Les méthodes peuvent également retourner des [!java|scala]tableaux[/!][!python]listes[/!] comme résultat sans aucun problème. Voici une méthode retournant [!java|scala]un tableau[/!][!python]une liste[/!] de la taille demandée après avoir initialisé toutes les cases à la valeur 42.

[!java]int[] remplir42(int taille) {
    int[] res = new int[taille];
    for (int i=0; i<taille; i++) 
        res[i] = 42;
    return res;
}[/!][!scala]def remplir42(taille:Int):Array[Int] = {
    var res = new Array[int] (taille)
    for (i <- 0 to taille -1) {
        res(i) = 42;
    }
    return res;
}[/!][!python]def remplir42(taille):
    res = []
    for i in range(taille):
        res.append(42)
    return res[/!]

Objectif de l'exercice

Enfin ! Après toutes ces explications, nous pouvons revenir à l'exercice.

Votre mission est plutôt simple au fond. Votre code doit sauvegarder le motif de couleurs observé sur la première colonne. Il faut bien entendu sauvegarder ces valeurs dans [!java|scala]un tableau[/!][!python]une liste[/!]. [!python]Le plus simple pour cela est de créer une liste vide puis d'y adjoindre (avec append() les différentes couleurs lues sur le sol de la première colonne (avec getCouleurSol()).[/!] [!java|scala]Pour cela, il faut déclarer et créer un tableau de variables de type Color. Attention, les différents mondes ne sont pas tous de la même taille et il faut utiliser getMondeHauteur() pour trouver la taille du monde courant. Une fois créé, remplissez le tableau en lisant les couleurs au sol de la première colonne (avec getCouleurSol()).[/!]

Une fois le motif de la première colonne lu et sauvegardé, il faut le répliquer sur toutes les colonnes, par exemple en exécutant getMondeLargeur() fois une méthode écrite tout exprès.