diff --git a/Tp6/associations.zip b/Tp6/associations.zip new file mode 100644 index 0000000000000000000000000000000000000000..d0bdeb1d8c6796026cdf8a32e602d3c46f493c16 Binary files /dev/null and b/Tp6/associations.zip differ diff --git a/Tp6/associations/ap_decorators.py b/Tp6/associations/ap_decorators.py new file mode 100644 index 0000000000000000000000000000000000000000..5f4adc9129d3d7ffef9d7dc68b59e4d6643d419d --- /dev/null +++ b/Tp6/associations/ap_decorators.py @@ -0,0 +1,135 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +""" +:module: ap_decorators +:author: FIL - Faculté des Sciences et Technologies - Univ. Lille <http://portail.fil.univ-lille1.fr>_ +:date: 2018, september + +""" + +from functools import wraps + + +def trace(fct): + ''' + Decorator for tracing every call to fct. + Recursive calls are indented. + + :Example: + + >>> @trace + ... def fact(n): + ... if n == 0: + ... return 1 + ... else: + ... return n * fact(n - 1) + + >>> fact(5) + -> fact((5,), {}) + ... -> fact((4,), {}) + ...... -> fact((3,), {}) + ......... -> fact((2,), {}) + ............ -> fact((1,), {}) + ............... -> fact((0,), {}) + ............... <- 1 + ............ <- 1 + ......... <- 2 + ...... <- 6 + ... <- 24 + <- 120 + 120 + ''' + @wraps(fct) + def wrapper(*args, **kwargs): + dots = '...' * wrapper.__depth + print('{:s} -> {:s}{:s}'.format(dots, wrapper.__name__, repr((args, kwargs)))) + wrapper.__depth += 1 + y = fct(*args, **kwargs) + wrapper.__depth -= 1 + print('{:s} <- {:s}'.format(dots, repr(y))) + return y + wrapper.__depth = 0 + return wrapper + +def count(fct): + ''' + decorator for counting calls to function fct + + :Example: + + >>> @count + ... def fact(n): + ... if n == 0: + ... return 1 + ... else: + ... return n * fact(n - 1) + + >>> fact.counter + 0 + >>> fact(5) + 120 + >>> fact.counter + 6 + ''' + @wraps(fct) + def wrapper(*args, **kwargs): + y = fct(*args, **kwargs) + wrapper.counter += 1 + return y + wrapper.counter = 0 + return wrapper + + +def memoize(fct): + ''' + decorator for memoizing computed values of function fct + + :Example: + + >>> @count + ... @memoize + ... def fact(n): + ... if n == 0: + ... return 1 + ... else: + ... return n * fact(n - 1) + + >>> fact.counter + 0 + >>> fact(5) + 120 + >>> fact.counter + 6 + >>> fact.counter = 0 + >>> fact(5) + 120 + >>> fact.counter + 1 + ''' + cache = dict() + @wraps(fct) + def wrapper(*args, **kwargs): + key = repr((args, kwargs)) + if key in cache: + return cache[key] + else: + y = fct(*args, **kwargs) + cache[key] = y + return y + return wrapper + + + +if __name__ == '__main__': + import doctest + doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS, verbose=False) + + + + + + + + + diff --git a/Tp6/associations/apl1test.py b/Tp6/associations/apl1test.py new file mode 100644 index 0000000000000000000000000000000000000000..8533ccaca5d99e7cfb83d6d86aa9334bb6a73a40 --- /dev/null +++ b/Tp6/associations/apl1test.py @@ -0,0 +1,89 @@ +import thonnycontrib +from thonnycontrib.backend.evaluator import Evaluator +import thonnycontrib.backend.l1test_backend +from thonny.plugins.cpython_backend.cp_back import MainCPythonBackend +import thonnycontrib.backend.doctest_parser +from thonnycontrib.backend.doctest_parser import ExampleWithExpected, ExampleWithoutExpected +import thonnycontrib.backend.ast_parser +from thonnycontrib.backend.ast_parser import L1DocTest +import thonnycontrib.backend.verdicts +from thonnycontrib.backend.verdicts.ExceptionVerdict import ExceptionVerdict + +import inspect +import tempfile +import os +import sys + +class MockBackend(MainCPythonBackend): + """ + Fake backend. + """ + def __init__(self): + ... + + def send_message(self, msg) -> None: + ... + +# register backend +thonnycontrib.backend.l1test_backend.BACKEND = MockBackend() + +def l1test_to_org(filename: str, source: str=""): + """ + Return an org abstract of the tests presents in `filename` file. + """ + abstract = {'total': 0, + 'success': 0, + 'failures': 0, + 'errors': 0, + 'empty': 0} + + if source == "": + with open(filename, 'rt') as fin: + source = fin.read() + evaluator = Evaluator(filename=filename, + source=source) + tests = evaluator.evaluate() + n = len(tests) + abstract['total'] = n + res = "" + for test in tests: + examples = test.get_examples() + res_examples = "" + nb_test, nb_test_ok = 0, 0 + empty = True + for example in examples: + verdict = test.get_verdict_from_example(example) + if isinstance(example, ExampleWithExpected): + nb_test += 1 + if verdict.isSuccess(): + nb_test_ok += 1 + abstract['success'] += 1 + else: + abstract['failures'] += 1 + empty = False + if isinstance(verdict, ExceptionVerdict): + abstract['errors'] += 1 + empty = False + res_examples += f"** {verdict}\n\n" + if not verdict.isSuccess(): + res_examples += f" {verdict.get_details()}\n\n" + if not empty: + res += f"* {test.get_name()} ~ {nb_test_ok}/{nb_test} réussis\n\n" + else: + abstract['empty'] += 1 + res += f"* {test.get_name()}\n\n Aucun test trouvé !\n\n" + res += res_examples + res = f"Tests exécutés : {abstract['total']}\nSuccès: {abstract['success']}, \ +Echecs: {abstract['failures']}, Erreurs: {abstract['errors']}, \ +Vide: {abstract['empty']}\n\n" + res + return res + + +def testmod(modulename: str): + """ + mimic the doctest.testmod function + for `modulename` module + """ + print(l1test_to_org(modulename)) + + diff --git a/Tp6/associations/association.py b/Tp6/associations/association.py new file mode 100755 index 0000000000000000000000000000000000000000..179219eeb0048df0ac72aa96dfbb63baf9ad1b74 --- /dev/null +++ b/Tp6/associations/association.py @@ -0,0 +1,89 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +""" +:mod:`association` module : un module pour les associations clé-valeur + +:author: `FIL - Faculté des Sciences et Technologies - + Univ. Lille <http://portail.fil.univ-lille1.fr>`_ + +:date: 2024 février + +""" +from ap_decorators import count +from typing import TypeVar + +# On définit deux types génériques : +# - C pour le type des clés +# - V pour le type des valeurs + +C = TypeVar('C') +V = TypeVar('V') + +class Association: + """Classe d'une association clé-valeur + """ + + def __init__(self, cle: C, valeur: V): + """ + $$$ asso1 = Association('a', 1) + $$$ asso1.cle + 'a' + $$$ asso1.valeur + 1 + """ + self.cle=cle + self.valeur=valeur + + def __repr__(self) -> str: + """ + $$$ repr(Association(2, 3)) + 'Association(2, 3)' + $$$ repr(Association('a', 1)) + "Association('a', 1)" + $$$ repr(Association((1, True), [1, 2, 3])) + 'Association((1, True), [1, 2, 3])' + """ + return f"Association{(self.cle,self.valeur)}" + + def __eq__(self, autre) -> bool: + """ + $$$ Association('a', 1) == Association('a', 1) + True + $$$ Association('a', 1) == Association('a', 2) + False + $$$ Association('a', 1) == Association(1, 'a') + False + $$$ AssertionError('a', 1) == ('a', 1) + False + """ + if isinstance(autre,Association): + return self.cle==autre.cle and self.valeur==autre.valeur + return False + +@count +def comp_asso(a1: Association, a2: Association) -> int: + """Renvoie 0 si les clés de a1 et a2 sont identiques + -1 si la clé de a1 < la clé de a2 + 1 si la clé de a1 > la clé de a2 + + Precondition : les clés de a1 et a2 sont comparables + + $$$ comp_asso(Association(1, 'a'), Association(1, 'c')) + 0 + $$$ comp_asso(Association(1, 'a'), Association(2, 'a')) + -1 + $$$ comp_asso(Association(1, 'd'), Association(0, 'c')) + 1 + """ + if a1.cle==a2.cle: + return 0 + elif a1.cle< a2.cle: + return -1 + else: + return 1 + +if __name__ == '__main__': + import apl1test + apl1test.testmod('association.py') + diff --git a/Tp6/associations/dicotrie.py b/Tp6/associations/dicotrie.py new file mode 100755 index 0000000000000000000000000000000000000000..b863fbbe9ed5fb6a7a277ad1cb226cf0794cfdc5 --- /dev/null +++ b/Tp6/associations/dicotrie.py @@ -0,0 +1,98 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +""" +:mod:`dicotrie` module : un module pour les ensembles d'associations + +:author: `FIL - Faculté des Sciences et Technologies - + Univ. Lille <http://portail.fil.univ-lille1.fr>`_ + +:date: 2024 février + +""" + +from association import Association, C, V, comp_asso +from recherches import indice_dicho, inserer +from types import NoneType + + +class DicoTrie: + """Classe d'une association clé-valeur + """ + + def __init__(self, liste_assos: list[Association]): + """ + """ + self.liste_assos=liste_assos + + def __repr__(self) -> str: + """ + $$$ repr(DicoTrie([Association('a', 1)])) + "DicoTrie([Association('a', 1)])" + $$$ repr(DicoTrie([Association('a', 1), Association('b', 2)])) + "DicoTrie([Association('a', 1), Association('b', 2)])" + $$$ repr(DicoTrie([Association('c', 3), Association('a', 2), Association('b', 1)])) + "DicoTrie([Association('a', 2), Association('b', 1), Association('c', 3)])" + """ + ... + + def __eq__(self, autre) -> bool: + """ + $$$ d1 = DicoTrie([Association("a", 1), Association("b", 2)]) + $$$ d2 = DicoTrie([Association("b", 2), Association("a", 1)]) + $$$ d3 = DicoTrie([Association("a", 1), Association("b", 2), Association("c", 3)]) + $$$ d1 == d2 + True + $$$ d1 == d3 + False + $$$ d1 == {"a": 1, "b": 2} + False + """ + ... + + def __setitem__(self, cle: C, valeur: V) -> NoneType: + """ + $$$ d1 = DicoTrie([Association("a", 1), Association("b", 2)]) + $$$ d1["c"] = 3 + $$$ d1 + DicoTrie([Association("a", 1), Association("b", 2), Association("c", 3)]) + """ + ... + + def __getitem__(self, cle: C) -> V: + """ + $$$ d1 = DicoTrie([Association("a", 1), Association("b", 2)]) + $$$ d1['a'] + 1 + $$$ d1['b'] + 2 + $$e d1['c'] + KeyError + """ + ... + + def __delitem__(self, cle: C) -> NoneType: + """ + $$$ d1 = DicoTrie([Association("a", 1), Association("b", 2)]) + $$$ del d1['a'] + $$$ d1 + DicoTrie([Association("b", 2)]) + $$e del d1['c'] + KeyError + """ + ... + + def __contains__(self, cle: C) -> bool: + """ + $$$ d1 = DicoTrie([Association("a", 1), Association("b", 2)]) + $$$ 'a' in d1 + True + $$$ 'c' in d1 + False + """ + ... + +if __name__ == '__main__': + import apl1test + apl1test.testmod('dicotrie.py') + diff --git a/Tp6/associations/mesures.py b/Tp6/associations/mesures.py new file mode 100644 index 0000000000000000000000000000000000000000..ae603e74002c835e970db392564c61f211ba57c5 --- /dev/null +++ b/Tp6/associations/mesures.py @@ -0,0 +1,98 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +""" +:mod:`mesures` module : un module pour les mesures de temps + +:author: `FIL - Faculté des Sciences et Technologies - + Univ. Lille <http://portail.fil.univ-lille1.fr>`_ + +:date: 2024 février + +""" +from dicotrie import DicoTrie +from association import Association +from types import NoneType +from timeit import timeit +from random import randrange +import matplotlib.pyplot as plt + +def generation_dict(tailles: list[int]) -> list[dict[str, NoneType]]: + """Renvoie une liste de dictionnaires dont les clés sont les + représentations des n entiers compris entre 0 et n-1 et les valeurs + associées None pour chaque entier n dans la liste tailles. + + Précondition : tous les entiers de tailles sont positifs ou nuls + + $$$ generation_dict([0, 2, 4]) + [{}, {'0': None, '1':None}, {'0': None, '1': None, '2': None, '3': None}] + """ + ... + +def generation_dicotrie(tailles: list[int]) -> list[DicoTrie]: + """Renvoie une liste de DicoTrie dont les clés sont les représentations + des n entiers compris entre 0 et n-1 et les valeurs associées None pour + chaque entier n dans la liste tailles. + + Précondition : tous les entiers de tailles sont positifs ou nuls + + $$$ generation_dicotrie([0, 2]) + [DicoTrie([]), DicoTrie([Association('0', None), Association('1', None)])] + """ + ... + +def mesure_temps_acces(taille : int, d: dict[str, NoneType] | DicoTrie) -> float: + """Renvoie le temps moyen mis pour accéder à 1000 éléments quelconques + du dictionnaire d. + + Précondition : les clés de d sont les représentations des entiers + compris entre 0 et taille - 1. + """ + cles_a_tester = [str(randrange(taille)) for _ in range(1000)] + stmt = "all(d[cle] == None for cle in cles_a_tester)" + return timeit(stmt = stmt, globals = locals(), number = 1) / 1000 + +def mesures_temps_acces_dict(tailles: list[int]) -> list[float]: + """Renvoie les listes des temps moyens pour accéder à 1000 éléments quelconques + de chacun des dictionnaires dont les clés sont les remprésentations des + n entiers compris entre 1 et n-1 pour chaque entier n dans la liste + tailles. + + Précondition : tous les entiers de tailles sont positifs ou nuls + """ + ... + +def mesures_temps_acces_dicotrie(tailles: list[int]) -> list[float]: + """Renvoie les listes des temps moyens pour accéder à 1000 éléments quelconques + de chacun des DicoTrie dont les clés sont les remprésentations des + n entiers compris entre 1 et n-1 pour chaque entier n dans la liste + tailles. + + Précondition : tous les entiers de tailles sont positifs ou nuls + """ + ... + + +if __name__ == '__main__': + import apl1test + apl1test.testmod('mesures.py') + + # Écrivez ici le code permettant de faire des hypothèses sur la + # vitesse d'accès aux dictionnaires. + + # linéaire + # tailles = list(range(0, 100000, 1000) + # temps_dict = mesures_temps_acces_dict(tailles) + # temps_dicotrie = mesures_temps_acces_dicotrie(tailles) + # plt.plot(tailles, temps_dict, '+') + # plt.plot(tailles, temps_dicotrie, '.') + # plt.show() + # log + # tailles = [2**n for n in range(16)] + # temps_dict = mesures_temps_acces_dict(tailles) + # temps_dicotrie = mesures_temps_acces_dicotrie(tailles) + # plt.plot(range(16), temps_dict, '+') + # plt.plot(range(16), temps_dicotrie, '.') + # plt.show() + + diff --git a/Tp6/associations/recherches.py b/Tp6/associations/recherches.py new file mode 100755 index 0000000000000000000000000000000000000000..fa01a60edb5f127fa4c6fc39cec77b295811ac45 --- /dev/null +++ b/Tp6/associations/recherches.py @@ -0,0 +1,117 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +""" +:mod:`recherches` module : un module pour les recherches + +:author: `FIL - Faculté des Sciences et Technologies - + Univ. Lille <http://portail.fil.univ-lille1.fr>`_ + +:date: 2024 février + +""" +from typing import TypeVar, Callable +from types import NoneType + +# On définit un type générique : +C = TypeVar('C') + +def indice_seq(elem: C, liste: list[C], comp: Callable[[C, C], int]) \ + -> tuple[bool, int]: + """Renvoie un couple (trouve, i) tel que: + - si elem est un élément de liste, + * trouve = True + * i est l'indice de première occurence de elem dans liste + - si elem n'est pas un élément de la liste : * trouve = False + * i = len(liste) + + Précondition : comp est une fonction de comparaison sur C + + $$$ def compare(x, y): return 0 if x == y else 1 if x > y else -1 + $$$ indice_seq(0, [1, 3, 5], compare) + (False, 3) + $$$ indice_seq(3, [1, 3, 5], compare) + (True, 1) + $$$ indice_seq(4, [1, 3, 5], compare) + (False, 3) + $$$ indice_seq(5, [1, 3, 5], compare) + (True, 2) + $$$ indice_seq(6, [1, 3, 5], compare) + (False, 3) + $$$ indice_seq(42, [], compare) + (False, 0) + """ + trouve=False + i=len(liste) + + for index, element in enumerate(liste): + if comp(elem,element)==0: + trouve=True + i=index + + return trouve,i +# +# def indice_dicho(elem: C, liste: list[C], comp: Callable[[C, C], int]) \ +# -> tuple[bool, int]: +# """Renvoie un couple (trouve, i) tel que: +# - si elem est un élément de liste, +# * trouve = True +# * i est l'indice de première occurence de elem dans liste +# - si elem n'est pas un élément de la liste : +# * trouve = False +# * pour tout j < i, liste[j] < liste[i] +# * pour tout j > i, liste[j] > liste[i] +# +# Précondition : comp est une fonction de comparaison et liste est triée pour comp +# +# $$$ def compare(x, y): return 0 if x == y else 1 if x > y else -1 +# $$$ indice_dicho(0, [1, 3, 5], compare) +# (False, 0) +# $$$ indice_dicho(3, [1, 3, 5], compare) +# (True, 1) +# $$$ indice_dicho(4, [1, 3, 5], compare) +# (False, 2) +# $$$ indice_dicho(5, [1, 3, 5], compare) +# (True, 2) +# $$$ indice_dicho(6, [1, 3, 5], compare) +# (False, 3) +# $$$ indice_dicho(42, [], compare) +# (False, 0) +# """ +# + + + +def inserer(indice: int, elem: C, liste: list[C]) -> NoneType: + """Insère l'élément elem à l'indice indice de la liste liste. + + Précondition : 0 ≤ indice ≤ len(liste) + + $$$ l = [1, 3, 5] + $$$ inserer(0, 0, l) + $$$ l + [0, 1, 3, 5] + $$$ inserer(4, 6, l) + $$$ l + [0, 1, 3, 5, 6] + $$$ inserer(3, 4, l) + $$$ l + [0, 1, 3, 4, 5, 6] + $$$ vide = [] + $$$ inserer(0, 42, vide) + $$$ vide + [42] + """ + if indice==len(liste): + liste.append(elem) + else: + liste.append(None) + for i in range (len(liste)-1, indice, -1): + liste[i]=liste[i-1] + liste[indice]=elem + + +if __name__ == '__main__': + import apl1test + apl1test.testmod('recherches.py') +