Aller au contenu

POO et récursivité

Modélisation d'appartements

Un appartement est constitué (en général) de plusieurs pièces. Dans ces pièces se trouvent un ou plusieurs meubles, qui occupent une surface au sol, diminuant ainsi la surface disponible dans la pièce. L'objectif de cet exercice est de représenter cette situation à l'aide de classes en python. Les questions de cet exercice forment un ensemble, mais il n'est pas nécessaire d'avoir répondu à une question pour aborder la suivante. En particulier, on pourra utiliser les méthodes des questions précédentes même quand elles n'ont pas été codées.

  1. On donne le code de la classe Meuble. L'attribut nom indique de quel meuble il s'agit (une armoire, un lit, etc.) ; l'attribut surface_sol represente la surface qu'occupe le meuble au sol, en mètre carrés.

    1
    2
    3
    4
    5
    class Meuble:
        def __init__(self, n, t):
            """ Meuble, str, float -> NoneType """
            self.nom = n
            self.surface_sol = t 
    

    Donner une instruction qui créé une variable lit de type Meuble, qui représente un lit nommé "Lit Queen Size" (longueur : 2m, largeur 1,6m).

    1
    lit = Meuble("Lit Queen Size", 3.2) 
    
  2. On représente une pièce d'un appartement à l'aide de la classe Piece. Ses attributs sont les suivants :

    • nom de type str ("chambre 1", "cuisine", par exemple).
    • superficie de type float. Il s'agit de la taille de la pièce en mètre carrés.
    • superficie_occupee, de type float. Vaut 0 initialement, augmente lorsque l'on ajoute des meubles.
    • liste_meubles, une liste de Meuble. Initialement vide, contient la liste des meubles présents dans la pièce.

    • Écrire le code python qui permet de définir une classe Piece possédant les attributs décrits dans l'énoncé. On écrira de plus la signature de la fonction __init__.

      1
      2
      3
      4
      5
      6
      7
      class Piece:
          def __init__(self, nom, superficie):
              """ Piece, str, float -> NoneType """
              self.nom = nom
              self.superficie = superficie
              self.superficie_occupee = 0
              self.liste_meubles = []
      
    • Écrire une méthode possede_place de la classe Piece qui renvoie True si et seulement si la pièce self possède au moins surface mètre carrés disponibles.

      1
      2
      3
      4
      def possede_place(self, surface):
          """ Piece, float -> bool
          Détermine si la pièce possède surface m2 disponibles """
          return self.superficie_occupee + surface < self.superficie
      
    • Écrire une méthode ajoute de la classe Piece, qui ajoute le meuble à la pièce self, en modifiant l'attribut liste_meubles. Cette méthode mettra également à jour l'attribut superficie_occupee en l'incrémentant de la surface au sol occupée par le meuble. Il est cependant possible qu'il n'y ait pas suffisamment d'espace dans la pièce pour accueillir le meuble. Dans ce cas l'attribut liste_meuble ne sera pas modifié. La méthode renverra True si le meuble a effectivement été ajouté à la pièce, False sinon.

      1
      2
      3
      4
      5
      6
      7
      8
      def ajoute(self, meuble):
        """ Pièce, Meuble -> bool """
        # if meuble.surface_sol + self.superficie_occupee < self.superficie:
        if self.possede_place(meuble.surface_sol):
            self.superficie_occupee += meuble.surface_sol
            self.liste_meubles.append(meuble)
            return True
        return False
      
  3. Un appartement contient une ou plusieurs pièces. On donne ci-dessous la définition de la classe Appartement :

    1
    2
    3
    4
    class Appartement:
        def __init__(self, c):
            """ Appartement, [Piece] -> NoneType """
            self.contenu = c
    
    1. On suppose que les variables de type Meuble suivants ont été correctement définies.

      1
      2
      3
      4
      5
      bain = Meuble("Baignoire", 1.2) 
      wc = Meuble("Toilettes", 0.4)
      lav = Meuble("Lavabo (toilettes)", 0.1)
      gaz = Meuble("Gazinière", 0.25)
      ev = Meuble("Évier (cuisine)", 0.15)
      

      Variable lit bain wc lav gaz ev
      Nom Lit Queen Size Baignoire Toilettes Lavabo (toilettes) Gazinière Évier (cuisine)
      Surface au sol 3.2 1.2 0.4 0.1 0.25 0.15

      Par exemple, la variable lit est de type Meuble, lit.nom est "Lit Queen Size" et lit.surface_sol vaut 3.2. À l'aide des variables décrites dans le tableau de l'énoncé, écrire le code python qui permet d'instancier une variable de type Appartement qui représente l'appartement dont on donne le plan ci-dessous.

      img

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      sdb = Piece("Salle de bain", 4.5) 
      sdb.ajoute(wc)
      sdb.ajoute(bain)
      sdb.ajoute(lav)
      sal = Piece("Salon", 27)
      coul = Piece("Couloir", 4)
      cuis = Piece("Cuisine", 3)
      cuis.ajoute(gaz)
      cuis.ajoute(ev)
      apt = Appartement([sdb, sal, coul, cuis])
      
    2. Écrire une méthode prix de la classe Appartement, qui étant donné un prix de vente au mètre carré, renvoie le prix de vente total de l'appartement. Celui-ci sera obtenu en calculant la superficie totale (disponible et occupée) de l'ensemble des pièces de l'appartement, que l'on multipliera par le prix au mètre carré passé en paramètre.

      1
      2
      3
      4
      5
      6
      7
      def prix(self, prix_m2):
          """ Appartement, float -> float
          Calcule le prix de vente de l'appartement. """
          s = 0
          for piece in self.contenu:
              s += piece.superficie
          return s*prix_m2
      
    3. Écrire une méthode recherche_place de la classe Appartement, qui étant donné une superficie (donnée en mètre carrés) renvoie la liste des pièces où on peut trouver superficie mètre carrés disponibles.

      1
      2
      3
      4
      5
      6
      7
      8
      def recherche_place(self, superficie):
          """ Appartement, float -> [Piece]
          Renvoie la listes des pièces de l'appartement avec plus de superficie mètre carrés disponibles. """
          l = []
          for piece in self.contenu:
              if piece.possede_place(superficie):
                  l.append(piece)
          return l
      
  4. Écrire une fonction recherche_appart qui étant donné une liste collection d'appartements dont le prix de vente au mètre carré est de 10 000€, renvoie l'appartement dont le prix de vente est le minimum parmi ceux qui possèdent au moins 4 pièces et dont l'une des pièces possède au moins 20 mètre carrés disponibles. Si aucun appartement de la liste collection ne satisfait les conditions demandées, alors la fonction renverra None. Toute trace de recherche, même incomplète, sera valorisée dans cette question.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def recherche_appart(collection):
        """ [Appartement] -> Appartement | NoneType """
        appart_mini = None
        prix_mini = float("inf")
        for appart in collection:
            if appart.prix(10000) < prix_mini and len(appart.contenu) >= 4 and len(appart.recherche_place(20)) > 0:
                appart_mini = appart
                prix_mini = appart.prix(10000)
        return appart
    

Des étoiles

  1. Écrire une fonction python etoiles qui étant donné un entier n renvoie la chaine de caractères constituée du caractère *, répété n fois. Toute syntaxe python est acceptée.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    def etoiles(n):
        """ int -> NoneType
        Affiche n fois le caractère *. Version récursive """
        if n == 0:
            return ""
        else:
            avant = etoiles(n - 1)
            return "*" + avant
    
    def etoiles(n):
        """ int -> NoneType
        Affiche n fois le caractère *. Version itérative """
        s = ""
        for _ in range(n):
            s += "*"
        return s
    
    def etoiles(n):
        """ int -> NoneType
        Affiche n fois le caractère *. Une ligne """
        return "".join(["*" for _ in range(n)])
    
    1
    2
    print(etoiles(2))
    print(etoiles(5))
    
    **
    *****
    

    Rappel. Si s1 et s2 sont deux chaînes de caractères, alors s1 + s2 renvoie la concaténation de s1 avec s2. Ainsi, "bonjour" + " ! " renvoie la chaîne "bonjour ! ".

  2. On donne le code des fonctions f, g, h et k suivantes.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    def f(n):
        """ int -> None """
        if n == 1:
            print(etoiles(1))
        else:
            print(etoiles(n))
            f(n - 1)
    
    def g(n):
        """ int -> None """
        if n == 1:
            print(etoiles(1))
        else:
            g(n - 1)
            print(etoiles(n))
    
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    def h(n):
        """ int -> None """
        if n == 1:
            print(etoiles(1))
        else:
            h(n - 1)
            print(etoiles(n))
            h(n - 1)
    
    def k(n):
        """ int -> None """
        if n == 1:
            print(etoiles(1))
        else:
            print(etoiles(n))
            k(n - 1)
            print(etoiles(n))
    

    On donne également, dans le désordre les affichages réalisés par les instructions f(3), g(3), h(3), et k(3).

    1
    h(3)
    
    *
    **
    *
    ***
    *
    **
    *
    
    1
    f(3) 
    
    ***
    **
    *
    
    1
    g(3) 
    
    *
    **
    ***
    
    1
    k(3) 
    
    ***
    **
    *
    **
    ***
    

    Sans justifier votre réponse, déterminer quelle instruction a provoqué quel affichage.

  3. On se propose d'étudier l'affichage réalisé par la fonction u suivante.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def u(n, i):
        """ int, int -> NoneType 
        On suppose que i <= n. """
        if i == n:
            print(etoiles(n))
        else:
            print(etoiles(i))
            u(n, i + 1)
            print(etoiles(i))
    
    1. Dresser l'arbre d'appel de l'instruction u(5, 2). On représentera les appels successifs les uns en dessous des autres, et on indiquera au bon endroit le long de l'arbre les différents événements d'affichage à l'aide de la ligne de code correspondante.

      img

    2. En déduire l'affichage réalisé par u(5, 2).

      1
      u(5, 2) 
      
      **
      ***
      ****
      *****
      ****
      ***
      **
      
    3. Quelle erreur soulève l'instruction u(2, 5) ? Expliquer votre réponse.

      u(2, 5) soulève l'erreur RecursionError, car le cas de base n'est jamais atteint : u(2, 5), appelle u(2, 6) qui appelle u(2, 7) etc. Ainsi, plus de 1000 appels seront réalisés ce qui déclenchera l'erreur.