From 091c07883d4708c12d1c6f96017d90916745cb9f Mon Sep 17 00:00:00 2001 From: Carol Alfonso Date: Fri, 3 Oct 2025 16:44:01 -0500 Subject: [PATCH 1/2] =?UTF-8?q?CAROL=20ALFONSO=20=E2=80=94=20Entrega=20Niv?= =?UTF-8?q?el=20Intermedio=20PRO=20.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 2001_carol_alfonso_v1.py | 70 +++++++++++++++++++++++++++++++ 2002_carol_alfonso_v1.py | 91 ++++++++++++++++++++++++++++++++++++++++ 2003_carol_alfonso_v1.py | 70 +++++++++++++++++++++++++++++++ 2004_carol_alfonso_v1.py | 71 +++++++++++++++++++++++++++++++ 2005_carol_alfonso_v1.py | 74 ++++++++++++++++++++++++++++++++ 5 files changed, 376 insertions(+) create mode 100644 2001_carol_alfonso_v1.py create mode 100644 2002_carol_alfonso_v1.py create mode 100644 2003_carol_alfonso_v1.py create mode 100644 2004_carol_alfonso_v1.py create mode 100644 2005_carol_alfonso_v1.py diff --git a/2001_carol_alfonso_v1.py b/2001_carol_alfonso_v1.py new file mode 100644 index 0000000..c2570d9 --- /dev/null +++ b/2001_carol_alfonso_v1.py @@ -0,0 +1,70 @@ +class Contact(): + def __init__(self, name: str, phone: int, email: str): + self.name = name + self.phone = phone + self.email = email + + def __str__(self): + return f"{self.name}, {self.phone}, {self.email}" + + +class ContactBook(): + def __init__(self): + self.contacts = [] + + def ADD(self, contact): + # No permitir emails duplicados + for c in self.contacts: + if c.email.lower() == contact.email.lower(): + return "ERROR:DUP" + self.contacts.append(contact) + return f"Contacto {contact.name} agregado." + + def DEL(self, name: str): + for contact in self.contacts: + if contact.name.lower() == name.lower(): + self.contacts.remove(contact) + return f"Contacto {name} eliminado." + return "Contact not found" + + def FIND(self, text: str): + results = [] + for contact in self.contacts: + if text.lower() in contact.name.lower() or text.lower() in contact.email.lower(): + results.append(str(contact)) + if results: + return "\n".join(results) + return "Contact not found" + + def LIST(self): + sorted_contacts = sorted(self.contacts, key=lambda c: c.name) + if not sorted_contacts: + return "No hay contactos." + return "\n".join(map(str, sorted_contacts)) + + +# ---- Casos de prueba ---- +Juan = Contact("Juan", 1151232528, "juan@gmail.com") +Ana = Contact("Ana", 1065498378, "ana@gmail.com") +Carlos = Contact("Carlos", 1156781234, "carlos@gmail.com") + +book = ContactBook() +print(book.ADD(Juan)) +print(book.ADD(Ana)) +print(book.ADD(Carlos)) +print(book.ADD(Contact("Pedro", 111222333, "juan@gmail.com"))) # Email duplicado + +print("\n--- LISTA INICIAL ---") +print(book.LIST()) + +print("\n--- ELIMINANDO ---") +print(book.DEL("Ana")) +print(book.DEL("Maria")) + +print("\n--- LISTA DESPUÉS ---") +print(book.LIST()) + +print("\n--- BUSQUEDA ---") +print(book.FIND("Carlos")) +print(book.FIND("gmail")) +print(book.FIND("Andrés")) diff --git a/2002_carol_alfonso_v1.py b/2002_carol_alfonso_v1.py new file mode 100644 index 0000000..6e504fc --- /dev/null +++ b/2002_carol_alfonso_v1.py @@ -0,0 +1,91 @@ +from decimal import Decimal, ROUND_HALF_UP + +class Course(): + def __init__(self, name: str, credits: int, grade: Decimal): + self.name = name + self.credits = int(credits) + self.grade = Decimal(str(grade)) + + def __str__(self): + return f"{self.name}, {self.credits}, {self.grade:.2f}" + + +class Semester(): + def __init__(self): + self.courses = {} # usamos diccionario para evitar duplicados + + def ADD(self, name: str, credits: int, grade: Decimal): + if name in self.courses: + print("ERROR:DUP") + else: + self.courses[name] = Course(name, credits, grade) + + def LIST(self): + sorted_courses = sorted(self.courses.values(), key=lambda c: (-c.credits, c.name)) + for course in sorted_courses: + print(course) + + def AVG(self): + total_credits = sum(c.credits for c in self.courses.values()) + if total_credits == 0: + avg = Decimal("0.00") + else: + total = sum(c.credits * c.grade for c in self.courses.values()) + avg = (total / total_credits).quantize(Decimal("0.00"), rounding=ROUND_HALF_UP) + print(f"prom={avg}") + + def IMPORT(self, lines): + valid, invalid = 0, 0 + for line in lines: + parts = line.strip().split(",") + if len(parts) != 3: + invalid += 1 + continue + name, credits, grade = parts + try: + credits = int(credits) + grade = Decimal(grade) + except: + invalid += 1 + continue + if name in self.courses: + invalid += 1 + else: + self.courses[name] = Course(name, credits, grade) + valid += 1 + print(f"Import: {valid} valid, {invalid} invalid") + + +# ---- Casos de prueba sencillos ---- +sem = Semester() + +# 1. Agregar cursos +sem.ADD("Matematicas", 4, Decimal("4.5")) +sem.ADD("Ingles", 2, Decimal("3.7")) +sem.ADD("Matematicas", 3, Decimal("5.0")) # duplicado → ERROR:DUP + +# 2. Listar cursos +print("\nLIST:") +sem.LIST() + +# 3. Promedio actual +print("\nAVG:") +sem.AVG() + +# 4. Importar desde lista CSV +lines = [ + "Fisica,3,5.0", + "Matematicas,2,4.0", # duplicado + "Quimica,4,4.2", + "Errorcito,xx,3.5" # inválido +] +sem.IMPORT(lines) + +# 5. Listar después de importar +print("\nLIST después de import:") +sem.LIST() + +# 6. Nuevo promedio +print("\nAVG nuevo:") +sem.AVG() + diff --git a/2003_carol_alfonso_v1.py b/2003_carol_alfonso_v1.py new file mode 100644 index 0000000..7b03228 --- /dev/null +++ b/2003_carol_alfonso_v1.py @@ -0,0 +1,70 @@ +class StackEmptyError(Exception): + pass + + +class MinStack: + """ + Implementación de una pila con: + - push(x): O(1) + - pop(): O(1) + - peek(): O(1) + - size(): O(1) + - min(): O(1) → devuelve el mínimo actual de la pila + """ + def __init__(self): + self.stack = [] # pila normal + self.min_stack = [] # pila auxiliar para mínimos + + def push(self, x): + self.stack.append(x) + # Si min_stack está vacío o x es menor/igual al actual mínimo + if not self.min_stack or x <= self.min_stack[-1]: + self.min_stack.append(x) + + def pop(self): + if not self.stack: + raise StackEmptyError("ERROR:EMPTY") + val = self.stack.pop() + if val == self.min_stack[-1]: + self.min_stack.pop() + return val + + def peek(self): + if not self.stack: + raise StackEmptyError("ERROR:EMPTY") + return self.stack[-1] + + def size(self): + return len(self.stack) + + def get_min(self): + if not self.min_stack: + raise StackEmptyError("ERROR:EMPTY") + return self.min_stack[-1] + + +# ---- Casos de prueba ---- +s = MinStack() + +# PUSH +s.push(5) +s.push(3) +s.push(7) +s.push(2) + +print("SIZE:", s.size()) # 4 +print("PEEK:", s.peek()) # 2 +print("MIN:", s.get_min()) # 2 + +# POP +print("POP:", s.pop()) # 2 +print("MIN después de pop:", s.get_min()) # 3 + +print("POP:", s.pop()) # 7 +print("MIN:", s.get_min()) # 3 + +print("POP:", s.pop()) # 3 +print("MIN:", s.get_min()) # 5 + +print("POP:", s.pop()) # 5 +# Intentar MIN o POP aquí lanzaría ERROR:EMPTY diff --git a/2004_carol_alfonso_v1.py b/2004_carol_alfonso_v1.py new file mode 100644 index 0000000..737d5e3 --- /dev/null +++ b/2004_carol_alfonso_v1.py @@ -0,0 +1,71 @@ +from collections import deque + +class QueueEmptyError(Exception): + pass + + +class Queue: + def __init__(self): + self.q = deque() # cola principal + self.total_wait = 0 # suma de tiempos de espera + self.served = 0 # elementos atendidos + self.K = None # límite de operaciones + self.ops = deque() # timestamps de operaciones (para control de rate limit) + + def setK(self, k): + self.K = k + + def _check_limit(self, timestamp): + if self.K is None: + return + # limpiar timestamps más viejos de 60s + while self.ops and timestamp - self.ops[0] >= 60: + self.ops.popleft() + if len(self.ops) >= self.K: + print("ERROR:RATE_LIMIT") + return False + self.ops.append(timestamp) + return True + + def ENQUEUE(self, id, timestamp): + if not self._check_limit(timestamp): + return + self.q.append((id, timestamp)) + + def DEQUEUE(self, timestamp): + if not self._check_limit(timestamp): + return + if not self.q: + raise QueueEmptyError("ERROR:EMPTY") + id, enq_time = self.q.popleft() + wait = timestamp - enq_time + self.total_wait += wait + self.served += 1 + return id + + def STATS(self): + L = len(self.q) + avg = (self.total_wait // self.served) if self.served > 0 else 0 + print(f"len={L} wait_avg={avg}") + + +# ---- Casos de prueba ---- +q = Queue() + +# Encolamos +q.ENQUEUE("A", 0) +q.ENQUEUE("B", 10) +q.ENQUEUE("C", 15) + +# Sacamos +print("DEQUEUE:", q.DEQUEUE(20)) # A, esperó 20s +print("DEQUEUE:", q.DEQUEUE(25)) # B, esperó 15s + +# Stats +q.STATS() # len=1, wait_avg=(20+15)/2 = 17 + +# Rate limit +q.setK(2) +q.ENQUEUE("D", 30) +q.ENQUEUE("E", 35) +q.ENQUEUE("F", 40) # debería dar ERROR:RATE_LIMIT diff --git a/2005_carol_alfonso_v1.py b/2005_carol_alfonso_v1.py new file mode 100644 index 0000000..297d713 --- /dev/null +++ b/2005_carol_alfonso_v1.py @@ -0,0 +1,74 @@ +class BiDictionary: + def __init__(self): + self.es_to_en = {} + self.en_to_es = {} + + def ADD(self, es: str, en: str): + es = es.casefold() + en = en.casefold() + if es in self.es_to_en or en in self.en_to_es: + print("ERROR:DUP") + return + self.es_to_en[es] = en + self.en_to_es[en] = es + + def FIND(self, word: str): + word = word.casefold() + if word in self.es_to_en: + print(f"es={word}, en={self.es_to_en[word]}") + elif word in self.en_to_es: + print(f"en={word}, es={self.en_to_es[word]}") + else: + print("NOTFOUND") + + def LIST(self): + for es in sorted(self.es_to_en.keys()): + print(f"{es} -> {self.es_to_en[es]}") + + def BULKADD(self, pairs): + # Validar primero + for es, en in pairs: + es, en = es.casefold(), en.casefold() + if es in self.es_to_en or en in self.en_to_es: + print("ERROR:BULK") + return + # Si no hubo conflicto, agregar todos + for es, en in pairs: + es, en = es.casefold(), en.casefold() + self.es_to_en[es] = en + self.en_to_es[en] = es + + def EXPORT(self, filename="diccionario.txt"): + try: + with open(filename, "w", encoding="utf-8") as f: + for es in sorted(self.es_to_en.keys()): + f.write(f"{es} -> {self.es_to_en[es]}\n") + print(f"Diccionario exportado a {filename}") + except Exception as e: + print("ERROR:EXPORT", e) + + +# ---- Casos de prueba ---- +bd = BiDictionary() + +bd.ADD("Casa", "House") +bd.ADD("Perro", "Dog") +bd.ADD("Gato", "Cat") + +print("\nLIST:") +bd.LIST() + +print("\nFIND:") +bd.FIND("casa") +bd.FIND("house") +bd.FIND("bird") + +print("\nBULKADD:") +bd.BULKADD([("Sol", "Sun"), ("Luna", "Moon")]) +bd.BULKADD([("Agua", "Water"), ("Perro", "Doggy")]) # ERROR:BULK + +print("\nLIST después de BULKADD:") +bd.LIST() + +print("\nEXPORT:") +bd.EXPORT("mi_diccionario.txt") From 61a65a789beeb27a19461fee6690d29e67c27f4b Mon Sep 17 00:00:00 2001 From: Carol Alfonso Date: Fri, 3 Oct 2025 16:44:43 -0500 Subject: [PATCH 2/2] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e19db3c..64fb39b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -

CodExpress — Nivel Intermedio (Estructuras + POO)

+

CAROL ALFONSO — Entrega Nivel Intermedio PRO .

+CodExpress — Nivel Intermedio (Estructuras + POO)

Tiempo: 3 horas · Meta: completar 7 u 8 mini-retos y, si te alcanza, una app integrada (código 2900) que reutilice tus propias clases y funciones. Lenguajes: Python, Java o C++ (usando POO).

Flujo de trabajo (fork & PR)