Pensamiento Computacional · Unidad 2 — Datos, expresiones y funciones
Cadenas de texto
Un string es una secuencia de caracteres numerados desde cero. Se puede medir, cortar y recorrer, pero no modificar.
La explicación
Cada carácter tiene una posición
Un string es una fila de casillas numeradas. La primera es la 0, no la 1.
posición: 0 1 2 3 4 5
carácter: p y t h o n
palabra[0] → "p" palabra[5] → "n"
Los índices negativos cuentan desde el final: palabra[-1] es el último
carácter. Es mucho más cómodo que palabra[len(palabra)-1].
Rebanadas
palabra[:3] → "pyt" desde el principio
palabra[3:] → "hon" hasta el final
palabra[::2] → "pto" de a dos
palabra[::-1] → "nohtyp" al revés
palabra[1:4] devuelve 4 − 1 = 3 caracteres. La resta
de los dos números te da el largo del pedazo.
Los strings no se modifican
Esto da error:
Un string es inmutable: una vez creado no se toca. Lo que se hace es armar uno nuevo:
Por eso todos los métodos de string devuelven uno nuevo en vez de modificar el original:
texto.lower() → todo en minúsculas
texto.replace("a","") → reemplaza (acá borra las "a")
texto.strip() → saca los espacios de los bordes
texto.split(",") → parte en una lista
len(texto) → cuántos caracteres tiene
texto.upper() por sí solo no cambia nada. Hay que guardar el resultado:
texto = texto.upper(). Escribir la línea suelta y esperar que el texto cambie es un
error clásico, y no avisa: simplemente no pasa nada.
Escribir una función que reciba una palabra y devuelva True si se lee igual al derecho y al revés.
La idea. Una palabra es capicúa si es igual a sí misma dada vuelta. Y dar vuelta un
string es [::-1]. Con eso, la función es una línea:
return palabra == palabra[::-1]
Probemos:
es_capicua("python") → False
Por qué funciona [::-1]. Los tres números de una rebanada son
desde : hasta : paso. Dejando los dos primeros vacíos se toma todo, y con paso −1 se
recorre para atrás.
limpia = frase.lower().replace(" ", "")
return limpia == limpia[::-1]
limpia: si sólo escribieras
frase.lower() sin asignar, no pasaría nada.
Control: probá siempre con un caso que dé True y otro que dé False. Una función que devuelve siempre lo mismo pasa desapercibida si sólo la probás con un caso.
Escribir una función que reciba una frase y devuelva True si es palíndromo, ignorando espacios, mayúsculas, acentos y signos de puntuación. "Anita lava la tina" y "Sé verlas al revés" tienen que dar True.
La comparación en sí es una línea. Todo el trabajo está antes: dejar la frase en una forma donde la comparación tenga sentido. Eso se llama normalizar, y conviene hacerlo en una función aparte.
Los tres problemas, en orden:
acentos: "é" y "e" son caracteres distintos
espacios: "anita lava la tina" invertido tiene los espacios en otro lado
La función que limpia:
frase = frase.lower()
for con, sin in (("á", "a"), ("é", "e"), ("í", "i"), ("ó", "o"), ("ú", "u")):
frase = frase.replace(con, sin)
return "".join(ch for ch in frase if ch.isalnum())
El orden importa: primero minúsculas, después acentos, y al final se filtra. Si se filtrara primero, las vocales acentuadas ya se habrían perdido o conservado según cómo las trate isalnum(), y el reemplazo no tendría nada que hacer.
Por qué isalnum() y no replace(" ", ""). Con replace se sacan los espacios pero quedan las comas, los puntos y los signos de pregunta. isalnum() se queda solo con letras y números, que es exactamente lo que se quiere comparar.
La comparación, con el corte de lista que invierte:
texto = limpiar(frase)
return texto == texto[::-1]
texto[::-1] es "el texto recorrido de a un paso hacia atrás", o sea al revés. Es la forma estándar en Python y conviene reconocerla de memoria.
Cómo se ve funcionando:
"Sé verlas al revés" → "severlasalreves" → True
"A mi loca Colima" → "amilocacolima" → True
"Hola mundo" → "holamundo" → False
"Roma no es amor" → "romanoesamor" → False
El último caso vale la pena mirarlo: "Roma no es amor" se lee al revés como "roma se on amor", que no es igual. La frase famosa es "Roma ni tibi subito motibus ibit amor", y en castellano el ejemplo que sí cierra es "Amor a Roma".
La versión sin cortes de lista, por si el parcial pide hacerlo con un ciclo:
texto = limpiar(frase)
for i in range(len(texto) // 2):
if texto[i] != texto[len(texto) - 1 - i]:
return False
return True
Esa versión recorre solo hasta la mitad, comparando el carácter i con el i-ésimo desde el final. Hace la mitad de las comparaciones y no construye ningún texto nuevo.
Dónde se pierde el punto: comparar frase == frase[::-1] sin limpiar. Con "Anita lava la tina" eso da False, y el código parece correcto: el error no está en la comparación sino en lo que se compara.
Dónde se cae la mayoría
- Contar desde 1. El primer carácter es el 0, y el último es len−1.
- Incluir el límite derecho de una rebanada: palabra[1:4] llega hasta la 3, no hasta la 4.
- Llamar a un método y no guardar el resultado. Los strings no se modifican, se reemplazan.
Hasta acá la explicación. La práctica es la otra mitad: 8 ejercicios de este tema, en cuatro niveles, que no te dicen sólo si está bien al final sino que te corrigen en cada paso y te explican por qué. Y uno para escribir el programa vos, que se prueba ejecutándolo en Python ahí mismo, en el navegador. Además trae una animación que muestra la idea en movimiento.
Practicar Cadenas de texto →Dónde entra este tema en el parcial
- Primer parcial — Trazar: texto, división entera y rebanadas10 puntos · resuelto paso a paso
Los otros temas de la unidad 2
Datos, expresiones y funciones