Saltar al contenido

Fundamentos de programación

Ciencias de la Computación Universitarias · Tema 1

1.1

Qué es este curso y cómo usarlo

Este es el primer curso de Informática Universitaria: fundamentos de programación , enseñado con Python 3. No se asume conocimiento previo de programación. Cada programa en este material está completo y ejecutable — escríbelo, ejecútalo, cambia una línea y predice qué cambios antes de volver a ejecutarlo. Ese ciclo de predecir-ajustar-ejecutar constituye toda la habilidad.

El curso es una escalera:

  • Las unidades de 1.1 a 1.3 construyen las mecánicas: valores y tipos, selección e iteración.
  • La unidad 1.4 encapsula el trabajo en funciones.
  • Las unidades 1.5 y 1.6 añaden las colecciones estándar y los archivos con manejo de errores.
  • La unidad 1.7 establece la disciplina superior: descomposición de problemas, pruebas y depuración.

Cómo leer cada unidad: una breve historia explica su importancia; una imagen o una regla general aparecen antes de cualquier sintaxis; luego las reglas precisas; después ejemplos resueltos que aumentan en dificultad, cada uno con un trazado que puedes verificar ejecutando el código; luego los errores reales que cometen las personas; luego un resumen de tres líneas. Cada ejemplo aquí puede verificarse de otra manera — ejecutándolo. Leer código sin ejecutarlo es como leer partituras musicales sin escucharlas. escuchándolo.

1.1

Valores, tipos y entrada/salida

Una aplicación de compras muestra "4.20" — ¿es texto o un número? Si es texto, sumar dos precios da "4.203.50"; si es un número, da 8.7. Cuál ocurre depende de el tipo, y todo en la unidad 1.1 aprende a identificar tipos antes de que ejecutes cualquier cosa.

Un valor tiene un tipo, y el tipo determina qué puedes hacer con él: int (números enteros), float (decimales), str (texto), bool (True/False). El type(...) función nombra el tipo de cualquier valor — pregúntalo libremente mientras aprendes.

age = 21              # int
price = 4.20          # float
name = "Ada"          # str
open_ = True          # bool
print(type(age))      # <class 'int'>

Variables son nombres vinculados a valores; la asignación age = 22 revincula el nombre — Los nombres son etiquetas en cajas; las expresiones leen cajas y crean nuevas cajas

Estado del programa como cajas: cada etiqueta de nombre apunta a su propia caja de valor; reasignar mueve la etiqueta, nunca edita la caja. La expresión 6 * 3.50 lee dos cajas y produce una nueva caja flotante 21.0
Los nombres son etiquetas en cajas; las expresiones leen cajas y crean nuevas cajas

Leer una expresión es "abrir las cajas, calcular, cerrar una nueva caja": count * 3.50 con count = 6 abre la caja int 6 y la caja float 3.50, y la regla de mezcla de Python promueve el int — el resultado es la caja float 21.0, nunca la caja int 21.

La conversión es explícita: int("21") es 21, pero "4" + 2 es un TypeError — Python se niega a adivinar si querías decir "42" o 6. Salida con print(...); entrada con input(...), que siempre devuelve un string, así que los números necesitan conversión:

born = int(input("Birth year? "))
print("About", 2026 - born, "years old")

Ejemplo resuelto 1 (la caja registradora del café). count = int(input("Coffees? ")), luego print("Total:", count * 3.50). En 6 cafés esto imprime Total: 21.0 — la caja .0 marca un float, porque int * float promueve a float. Observar qué tipo sale de una expresión es el hábito completo de la unidad 1.1. Rastrea el estado así: input devuelve la cadena "6"; int(...) abre una nueva caja que contiene la int 6; la multiplicación hace una nueva caja float 21.0; print lo muestra.

Ejemplo resuelto 2 (el accidente clásico, reparado). Un impresor de banderas une un conteo a un mensaje:

count = int(input("Tickets? "))
print("Sold " + count + " today")

Ejecútalo y Python eleva TypeError: can only concatenate str (not "int") to str — la regla se niega a adivinar. La reparación nombra la conversión explícitamente: print("Sold " + str(count) + " today"), o el más limpio print("Vendido", count, "hoy"), which inserts the spaces for you. The lesson to carry: + significa suma para números, concatenación para cadenas, y nunca una mezcla silenciosa.

Errores comunes. Olvidar que input() siempre devuelve una cadena, por lo tanto input() * 2 repite texto en lugar de duplicar un número. Escribir 4.20 + 6 y esperar una respuesta de int: cualquier punto flotante en la expresión produce uno. Confiar 0.1 + 0.2 == 0.3 (es False; redondeo binario — la unidad 1.2 muestra la corrección).

Resumen: cuatro tipos básicos determinan cada operación; los nombres son etiquetas, la asignación reasigna; las expresiones mezclan tipos mediante promoción con puntos flotantes; las conversiones son explícitas y input() necesita una.

Vocabulario Entrenar
English Español
programming fundamentals/ˈprəʊɡræmɪŋ ˌfʌndəˈmentlz/ fundamentos de programación
type/taɪp/ Tipo
variables/ˈveərɪəblz/ variables
string/strɪŋ/ Cadena
selection/sɪˈlekʃn/ Selección
iteration/ˌɪtəˈreɪʃn/ iteración
infinite loop/ˈɪnfɪnət luːp/ Bucle infinito
trace table/treɪs ˈteɪbl/ Tabla de seguimiento
function/ˈfʌŋkʃn/ función
return value/rɪˈtɜːn ˈvæljuː/ valor de retorno
parameter/pəˈræmɪtə/ parámetro
set/set/ conjunto
1.2

Selección: elegir entre rutas

Un torniquete de parque temático debe cobrar diferente a niños, adultos y ancianos — un programa, varias rutas. Selección es cómo elige un programa: if, elif, else ejecuta un bloque solo cuando una condición se cumple.

Diagrama de flujo de las reglas de precios del cine con la verificación de edad antes que la de estudiante; un estudiante de 70 años toma el primer ramo verdadero y paga 5
Edad 70, estudiante: la primera rama verdadera decide el precio

La condición es cualquier expresión booleana; las comparaciones (==, !=, <, >=) y la lógica (and, or, not) la construyen. Dos reglas hacen que toda decisión sea legible:

  1. Las ramas se evalúan de arriba hacia abajo; la primera rama verdadera gana y el resto omitido.
  2. La indentación no es decoración: ES el bloque. Un espacio adicional cambia a qué líneas pertenece la rama.
temp = 37.5
if temp >= 38:
    print("Fever")
elif temp >= 36 and temp < 38:
    print("Normal")
else:
    print("Low")

Ejemplo resuelto 1 (precio de cine). Bajo 12 o 65+ se paga 5, los estudiantes pagan 7, el resto de las personas 10:

age = int(input("Age? "))
student = input("Student? y/n ") == "y"
if age < 12 or age >= 65:
    price = 5
elif student:
    price = 7
else:
    price = 10
print("Pay", price)

El orden importa: la primera condición verdadera gana, por eso la verificación de edad precede a la comprobación del estudiante — un estudiante de 70 años debe pagar 5, y este orden lo indica. Ejecuta con 70 y y para ver la rama más a la izquierda del diagrama de flujo tomada.

donde cada 2 solo necesita una comparación, porque las pruebas anteriores ya han donde cada elif solo necesita una comparación, porque las pruebas anteriores ya han fallado:

score = int(input("Score? "))
if score >= 80:
    band = "A"
elif score >= 70:
    band = "B"
elif score >= 60:
    band = "C"
else:
    band = "F"
print("Band:", band)

aviso: el 79 no necesita también Band: B; llegar a esa note: la elif score >= 70 no necesita también and score < 80 — llegar a esa línea ya demuestra que la puntuación está por debajo de 80. Confiar en la caída como esto es lo que hace cortas las cadenas; reevaluar todo es la versión más larga y errónea del principiante. versión.

Floating-point comparisons need care: 0.1 + 0.2 == 0.3 is False (binary rounding), so compare against a tolerance: abs(x - y) < 1e-9.

Errores comunes. Escribir = donde se pretendía == — el error clásico, y Python lo rechaza en condiciones (SyntaxError) precisamente porque es tan común. Probar el caso especial después del general, por lo que el general siempre prevalece. Usando elif con una condición que nunca se alcanza porque un ramo anterior ya la capturó.

Resumen: las condiciones eligen rutas, gana el primero verdadero, la indentación define el bloque; ordenar pruebas de específico a general; los floats se comparan por tolerancia, nunca por ==.

1.3

Iteración: repetirlo nuevamente

Pagar facturas de 5,000 a mano es una carrera; con un bucle es un programa. Iteración repite trabajo: un bucle for recorre una secuencia conocida; un bucle while se ejecuta mientras una condición se cumple. La pregunta de diseño siempre es: ¿sabes cuántas veces se repetirá, o decide una condición?

total = 0
for i in range(1, 101):      # 1..100 — range stops BEFORE its end
    total += i
print(total)                  # 5050

Usa for cuando el número de repeticiones sea conocido; usa while cuando una condición decida. Cada bucle while necesita tres cosas visibles en el código: el valor inicial de la variable, la condición, y algo dentro que avance hacia la salida — de lo contrario es un bucle infinito. bucle.

Bucle while del factorial junto a su tabla de trazado: n y factorial después de cada ronda, desde 5 y 1 hasta 1 y 120, cuando la condición se vuelve Falsa
Tabla de trazado para el bucle factorial
n = 5
factorial = 1
while n > 1:
    factorial *= n
    n -= 1                    # the step toward exiting
print(factorial)              # 120

La tabla de trazado es el hábito de verificación del curso para los bucles: sigue las variables ronda por ronda, primero en papel. Para el bucle factorial: (n=5, f=1) → (4, 5) → (3, 20) → (2, 60) → (1, 120). Construye la tabla a mano, luego agrega un print(n, factorial) dentro del bucle y deja que Python te califique.

Ejemplo resuelto 2 (una búsqueda con break, escalada). Encuentra el primer múltiplo de 7 que es mayor que 100 — el conteo de viajes es desconocido, la condición decide:

n = 100
while True:
    if n % 7 == 0:
        break                 # found it — leave the loop now
    n += 1
print(n)                      # 105

La traza es corta: 100, 101, …, 104 fallan todos la prueba; 105 pasa y break sale. break sale del bucle temprano; continue salta a la siguiente ronda. Ambos son herramientas de legibilidad — un bucle que debe escanear hasta encontrar algo, luego detenerse, se lee mejor con break. La escalada adicional, duplicar el dinero: a 5% al año, ¿cuánto tiempo tarda 1000 en alcanzar 2000?

balance = 1000.0
years = 0
while balance < 2000.0:       # the condition itself is the goal
    balance *= 1.05
    years += 1
print(years, round(balance, 2))   # 15 2078.93

Quince años, y el bucle se detiene en el momento exacto en que cambia la condición — la tabla de trazado mostraría el saldo cruzando la línea en 2078.93, no aterrizando exactamente en 2000.

Errores comunes. Editar la lista (unidad 1.5) o la variable del bucle que estás for-recorriendo. range(1, 100) cuando se pretendía 100 — el final está excluido. Un while cuyo paso de salida está ausente o comentado: la condición nunca cambia, y el programa se congela.

Resumen: for para conteos de viaje conocidos, while para condiciones; todo bucle muestra inicio, condición y paso; traza en papel, luego impresión de nivel examen; break y continue para salidas tempranas limpias.

Vocabulario Entrenar
English Español
list/lɪst/ lista
dictionary/ˈdɪkʃənəri/ diccionario
tuple/ˈtuːpl/ tupla
1.4

Funciones: nombrar un trabajo una sola vez

El mismo cálculo de precio aparece en cinco lugares; la regla de impuestos cambia; cuatro de los cinco se actualizan. Una función empaqueta un trabajo detrás de un nombre para que se escriba una sola vez y confiable en todas partes: parámetros de entrada, un valor de retorno de salida. def define; llamar ejecuta.

def total_price(count, unit=3.50):
    """Return the total for count items at unit price."""
    return count * unit

print(total_price(6))            # 21.0
print(total_price(6, unit=4.0))  # 24.0

La definición proporciona un parámetro (count, unit); la llamada suministra un argumento (6). Un valor por defecto (unit=3.50) hace que el argumento sea opcional. return devuelve un valor y termina la llamada; imprimir no es devolver — una función sin return retorna None, y el invocador que intenta realizar operaciones aritméticas sobre él recibe un TypeError.

Tres marcos de llamada: print llama a order_total(250, 4), lo cual se pausa mientras add_fee(1000) se ejecuta; 1003 se devuelve a lo largo de la cadena
Llamadas anidadas: el invocador se pausa hasta que el valor regresa

Cada llamada tiene su propio ámbito local: los nombres dentro son invisibles fuera, y desaparecen cuando finaliza la llamada. Una traza de llamadas anidadas muestra: invocador se pausa → callee se ejecuta → el valor de retorno cae en el invocador.

Ejemplo resuelto 1 (el pedido con tasa incluida).

def add_fee(amount):
    """Return amount plus a fixed 3 pence fee."""
    return amount + 3

def order_total(unit_pence, quantity):
    """Return the order total: subtotal plus the fixed fee."""
    return add_fee(unit_pence * quantity)

print(order_total(250, 4))   # 1003

El contrato en el docstring es importante: enteros de pence entran, enteros de pence salen. Al rastrear order_total(250, 4): subtotal 1000, add_fee retorna 1003 — y el diagrama de marco de arriba muestra la pausa y la devolución.

Ejemplo resuelto 2 (un algoritmo real dentro de una función pequeña, escalado). is_prime(n) retorna True cuando nada desde 2 hasta $\sqrt{n}$ divide n:

def is_prime(n):
    if n < 2:
        return False
    d = 2
    while d * d <= n:        # only test up to the square root
        if n % d == 0:
            return False     # found a divisor — answer known, stop
        d += 1
    return True

print([p for p in range(2, 30) if is_prime(p)])
# [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

Dos hábitos de función se encuentran aquí: una temprana return termina la llamada en el momento en que la respuesta es conocida (no se necesita variable de bandera), y el límite del bucle d * d <= n proviene de la derivación — si $n = a \times b$ con $a \le b$, entonces $a \le \sqrt{n}$, por lo que probar más allá de $\sqrt{n}$ vuelve a verificar divisores ya vistos. Ejecútalo: la lista son exactamente los primos menores que 30.

Errores comunes. Imprimir dentro de una función en lugar de devolver, por lo que el resultado no se puede reutilizar. Olvidar el return en una rama — ese camino silenciosamente produce None. Acceder a las variables locales de una función desde fuera; desaparecen cuando la llamada termina.

Resumen: def una vez, llámalo en cualquier lugar; parámetros entran, un valor sale mediante return; las devoluciones tempranas terminan la llamada; las variables locales nacen y mueren con la llamada; los docstrings definen el contrato.

Vocabulario Entrenar
English Español
argument/ˈɑːɡjuːmənt/ argumento
local scope/ˈləʊkl skəʊp/ ámbito local
1.5

Colecciones: listas, diccionarios, tuplas, conjuntos

La caja registradora de un café necesita los pedidos del día en orden, el menú por nombre, una ubicación que no debe variar y las bebidas únicas de hoy. Cuatro tareas, cuatro contenedores — elegir entre ellos es la decisión de diseño de la unidad 1.5.

Una lista es una secuencia ordenada y mutable: marks = [72, 85, 61], indexada desde 0, cortada con marks[1:3]. Un diccionario mapea claves a valores: price = {"flat white": 3.5, "tea": 2.8}, leído y escrito por clave. Una tupla es una secuencia fija (53.1, -1.5) — adecuada para coordenadas que no deben cambiar. Un conjunto contiene elementos únicos sin orden: {"a", "b"}.

marks = [72, 85, 61]
marks.append(90)               # [72, 85, 61, 90]
best = max(marks)              # 90
average = sum(marks) / len(marks)   # 77.0
Los nombres a y b apuntan ambos a un mismo objeto lista, que contiene 1 y 2 después de b.append(2)
Aliasing: dos nombres, una lista

La asignación nunca copia: a = [1]; b = a; b.append(2) hace que a sea [1, 2] — la lista se comparte, los nombres no. Este aliasing son nuevamente las cajas de la unidad 1.1, ahora con dos etiquetas en una sola caja, y también explica la siguiente regla: mutar una lista mientras se recorre omite elementos; recorra una copia (for m in marks[:]) o construya una nueva lista.

Ejemplo resuelto 1 (recuento por categoría).

orders = ["tea", "flat white", "tea", "tea", "mocha"]
tally = {}
for item in orders:
    tally[item] = tally.get(item, 0) + 1
print(tally)   # {'tea': 3, 'flat white': 1, 'mocha': 1}

El diccionario .get(key, 0) proporciona 0 para un primer avistamiento — la idiomática forma que hace del conteo una línea de código. Verifique manualmente: "tea" aparece tres veces en orders, y el recuento impreso así lo indica.

Ejemplo resuelto 2 (el artículo más vendido del día, escalado). Basándose en el recuento, informe el elemento principal y su cuenta:

best = max(tally, key=tally.get)
print(best, tally[best])       # tea 3

El argumento key=tally.get le pide a max que compare los elementos por sus cuentas en lugar de alfabéticamente — el mismo truco con key= clasifica cualquier colección según cualquier métrica. La escalación es importante: los programas reales encadenan estas herramientas (contar, luego clasificar) en lugar de escanear orders dos veces.

Elegir la colección es la decisión de diseño: orden + duplicados → lista; búsqueda por nombre → diccionario; agrupación fija → tupla; unicidad → conjunto.

Errores comunes. tally[item] += 1 en una clave que aún no está en el diccionario (KeyError) — .get es la solución. Esperar que un conjunto o diccionario recuerde el orden. Asumir que b = a copia una lista; en realidad crea un alias.

Resumen: cuatro contenedores para cuatro tareas; la asignación comparte referencias, nunca copia; .get hace el conteo seguro; key= convierte el ranking en una sola línea.

Vocabulario Entrenar
English Español
aliasing/ˈeɪlɪəsɪŋ/ aliasing
1.6

Archivos y errores

Se cierra el café, se corta la electricidad —y el conteo de la mañana sobrevive solo si el programa lo escribió en disco. Un archivo perdura más que el programa. with open(path) as f: opens, closes on exit, and reads text; open(path, "w") escribe (y primero lo vacía —el accidente clásico).

with open("log.txt", "w") as f:
    f.write("opened\n")

with open("log.txt") as f:
    for line in f:
        print(line.rstrip())     # rstrip removes the trailing newline

El bloque with es la barandilla de seguridad: suceda lo que suceda dentro, el archivo se cierra. La lectura devuelve líneas con su salto de línea, por lo que .rstrip() pertenece a cada bucle.

Los errores son valores que Python lanza como excepciones: FileNotFoundError, ValueError, KeyError, TypeError. try/except captura un tipo nombrado — nunca un except: desnuda que oculta fallos reales.

Ejemplo resuelto 1 (el archivo de configuración ausente). Leer un tamaño de texto desde un archivo de configuración que puede estar ausente, o que no contiene un número entero:

try:
    with open("settings.txt") as f:
        size = int(f.read().strip())
except FileNotFoundError:
    size = 12                    # no file yet: a sensible default
except ValueError:
    size = 12                    # the file holds "big", not a number
print("Text size", size)

Ejecútelo tres veces: con 16 en el archivo, sin archivo, y con big en el archivo; imprime un tamaño utilizable cada vez, procedente de tres rutas distintas. Esa es la estructura del código robusto: una decisión sensata varios caminos hacia él. Maneja el error que esperas, donde puedas responder a él; deja que el resto falle ruidosamente.

Un lector de configuración: el bloque try abre, lee y convierte el valor; funciona correctamente, o bien una rama de FileNotFoundError o ValueError establece el tamaño por defecto 12; las tres vías convergen y el programa continúa

Ejemplo resuelto 2 (un bucle de entrada robusto, escalado). Pide hasta que el usuario escriba un número entero:

def read_count():
    while True:
        try:
            return int(input("How many? "))
        except ValueError:
            print("Whole numbers only.")

print(read_count())

Pásale x luego 4: el primer intento falla, es capturado, imprime el mensaje, y el while True vuelve a preguntar; el segundo intento de int(...) tiene éxito y return sale el bucle y la función en un solo paso. Tres unidades se combinan aquí: el bucle (1.3), el contrato de la función (1.4) y la excepción (1.6) — que es el objetivo de la escalada.

Common mistakes. open(path, "w") on the wrong path: the file is created or emptied before you notice. Catching Exception broadly and continuing with broken Olvidar .rstrip(), por lo que la salida muestra líneas con doble espacio.

Resumen: with open(...) tanto para lectura como para escritura; "w" vacía primero; las excepciones son valores: captura el tipo nombrado que esperas, recurre a una solución razonable y deja que el resto falle. the rest crash.

Vocabulario Entrenar
English Español
value/ˈvæljuː/ Valor
file/faɪl/ archivo
exceptions excepciones
decomposition/ˌdiːkɒmpəˈzɪʃn/ descomposición
testing/ˈtestɪŋ/ pruebas
debugging/ˈdiːbʌɡɪŋ/ depuración
1.7

Descomposición, prueba y depuración

diferencia no es la velocidad de escritura — es que el código del equipo rápido está construido a partir de piezas pequeñas que pueden demostrar su corrección individualmente, mientras que el equipo lento depura toda la aplicación de una vez. la diferencia no es la velocidad de escritura, sino que el equipo rápido construye su código a partir de pequeños componentes que pueden demostrar individualmente que son correctos, mientras el equipo lento depura toda la aplicación de una sola vez. Un1.7 que nunca se activa es una prueba que pasa. Escribe las pruebas antes de escribir el

suficiente para probarse por separado. Pruebas comparan la salida real de una función con la esperada, en los límites: cero, uno, vacío, negativo, enorme. se espera, en los límites: cero, uno, vacío, negativo, enorme.

El bucle guiado por pruebas: escribe una prueba fallida que establezca el contrato; ejecútala y verás que falla por la razón correcta; escribe el cuerpo más simple que pase; vuelve a ejecutar hasta que esté verde; refactoriza con las pruebas manteniendo la forma
El bucle guiado por pruebas: rojo, verde, refactorización
def add_fee(amount):
    return amount + 3

def order_total(unit_pence, quantity):
    return add_fee(unit_pence * quantity)

assert order_total(250, 4) == 1003
assert order_total(250, 0) == 3      # the zero-quantity boundary
assert order_total(0, 4) == 3

Un assert que nunca se dispara es una prueba aprobada. Escriba las pruebas antes de escribir el cuerpo cuando sea posible — son el contrato hecho ejecutable, y el bucle anterior (rojo → verde → refacturar) es que ese hábito se ejecute continuamente.

Ejemplo resuelto 1 (ampliar las pruebas con la función). Amplíe order_total con un descuento de 10% sobre £10: la prueba se escribe primero, en el nuevo límite:

def order_total(unit_pence, quantity):
    subtotal = add_fee(unit_pence * quantity)
    if subtotal >= 1000:             # pence: over £10
        subtotal = int(subtotal * 0.9)
    return subtotal

assert order_total(200, 4) == 803    # under £10 — unchanged
assert order_total(250, 4) == 902    # 1003 pence → 10% off → 902.7 → 902
assert order_total(250, 5) == 1127   # 1253 pence → 1127.7 → 1127

Verifique manualmente la prueba intermedia: subtotal 1003, diez por ciento de descuento da 902.7, y int(...) se redondea hacia abajo a 902. Observe también la primera afirmación: en 200 peniques y cuatro tazas, el total permanece en 803: las pruebas de límite demuestran tanto que el descuento se activa como que no se activa prematuramente.

Ejemplo resuelto 2 (bisecar un error con impresiones, escalonado). El recuento de 1.5 imprime {'tea': 3}, pero la línea del producto más vendido imprime la bebida incorrecta. El método: (1) reproducir con la entrada fallida más pequeña; (2) imprimir los valores en el límite entre la parte confiable y la sospechosa: aquí, el recuento antes de la max; (3) corregir una cosa; (4) ejecutar las pruebas nuevamente. Mover la impresión a mitad del código en cada ejecución reduce a la mitad la búsqueda: cuatro impresiones localizan un error en un programa de dieciséis líneas, sin importar cuán bien esté oculto.

Depuración es encontrar la diferencia entre lo que usted cree que hace el código y lo que realmente hace: la impresión en el límite de confianza/sospecha es el instrumento.

Errores comunes. Probar solo el camino feliz: los límites (cero, uno, vacío, negativo) son donde los programas fallan. Escribir una semana de código antes de la primera prueba, luego depurar una montaña. Corregir dos cosas a la vez, para que ningún resultado signifique nada.

Resumen: descomponga hasta que cada pieza se pruebe por separado; establezca los límites en cuanto exista una función. Escriba la prueba primero cuando pueda; depure mediante bisección con impresiones de confianza en los límites — el hábito sobre el que se basa toda la carrera. en los límites de confianza — el hábito en el que se basa toda la titulación.

Lecciones interactivas sobre este tema

Trátalo paso a paso, con ejercicios de verificación instantánea.

Más temas en Ciencias de la Computación Universitarias

Iniciar sesión o crear cuenta

IGCSE, A-Level & AP