[MÚSICA DE FONDO]
DAVID MALAN: Muy bien.
Estamos en CS50 y este es el día previo a nuestra prueba.
Esta es la lección 8, en la cual entraremos
por fin en la transición de C, este lenguaje de bajo nivel
al que dedicamos bastante tiempo.
El objetivo de hoy no es enfocarnos en Python como tal,
honestamente, esperamos que sea uno de los aspectos que más fortalezcan
al curso, es decir, enfatizar que este no ha sido un aprendizaje semestral en C,
sino un semestre en el que aprendimos programación,
un tipo de programación llamado programación imperativa o por procedimientos.
Pero quizás verán más sobre esto en otro curso de programación de alto nivel.
En este curso se trata, básicamente, de enseñarles
a aprender nuevos lenguajes por sí mismos.
De hecho encontrarán, mientras exploramos algunas características
y la sintaxis de Python, que probablemente les parecerá
tan complejo como les parecía C hace algunas semanas.
Pero una vez que comiencen a reconocer los patrones,
como lo hicieron con C, todo será más accesible y más útil
para la resolución de problemas.
No está relacionado, pero al inicio de la semana estaba en Mountain View
con parte del equipo.
Y tal vez recuerden que en la última conferencia en Harvard
les ofrecimos una vista de los primeros racks de servidores
que tenía Google.
Bueno, resulta que se mudaron de edificio
y nosotros nos topamos con toda la exhibición.
Así que esto que les muestro es una foto que tomé con mi propio teléfono
y verlo fue realmente genial.
Pueden ver en el interior los viejos discos duros que utilizaban
y también pudimos observar algunas de las etiquetas.
Y de hecho, los discos duros se fabricaron en 1999,
que es cuando Google comenzó a obtener algo de su impulso.
Aquí pueden ver las tarjetas verdes de circuitos
de lo que probablemente serían los CPU y otras cosas.
Así que si quieren recordar viejos tiempos,
no duden en leer sobre esto en Wikipedia o incluso en los fragmentos que ven aquí.
Y entonces, curiosamente, en la conferencia en la que algunos estábamos
descubrimos esto, quizás sea el mayor pato depurador que se haya creado alguna vez
a partir de depuradores de pato más pequeños, uno de los cuales era nuestro.
Y eso fue lo que vimos la semana pasada.
Muy bien.
Entonces, ¿qué vamos a hacer esta semana y las que vienen?
Recordarán que cuando hicimos la transición de Scratch a C,
hicimos un par de comparaciones entre la sintaxis y las características.
Y pensé que sería útil adoptar aquí la misma perspectiva
para enfatizar que la mayoría de las ideas que hoy exploraremos
no son nuevas en sí,
solo se trata de cómo las expresamos y cómo
escribimos la sintaxis en el lenguaje conocido como Python,
la cual será diferente a la de Scratch o a la de C,
y ahora aquí estamos con Python.
Y si se acuerdan de la semana 0, cuando querían decir algo en Scratch,
utilizaban esa pieza púrpura del rompecabezas para decir hola.
Y la llamamos una función o una declaración.
Era una especie de acción verbal.
Y en C, por supuesto, se parecía un poco a algo como esto.
De ahora en adelante, a partir de hoy, se verá así en Python.
antes y después,
antes y después.
Es bastante fácil diferenciar visualmente estas dos cosas.
Pero, ¿cuáles son las dos diferencias
que de inmediato nos saltan a la vista?
C, Python.
Al parecer, ya no hay \ n en este contexto,
así que es un alivio no tener que escribirlo más.
¿Qué otra cosa parece diferente?
No más punto y coma, gracias al cielo.
¿Cierto?
Tal vez sea la fuente de frustración más tonta
que hayamos experimentado al omitir uno de esos.
¿Alguien más por aquí?
Sí, que printf ahora solo es print, lo cual, de por sí, es bastante razonable.
Son diferencias sumamente pequeñas,
pero son una especie de testamento de los ajustes mentales
que tendremos que hacer.
Por fortuna, hemos visto que podemos dejar estas cosas
y este es el principio básico de Python, ya que uno de sus objetivos es que
sea más fácil de escribir que algunos de sus predecesores, entre ellos C.
Así, podríamos implementar en C este hello, world, en el programa que
se ejecutó al hacer clic en la bandera verde, con un código como el de la derecha.
Y para aquellos que no tenían experiencia en programación, esto significó llegar
a CS50, a lo que probablemente se veía como el idioma griego para ustedes,
hace solo unas semanas
y desciframos lo que todas esas líneas significaban.
Pero en Python, ¿adivinen qué?
Si queremos escribir un programa cuyo propósito en la vida es decir hello,
bueno, solo escribimos def main ( ):
print ( "hello, world" )
es una estructura algo similar,
de hecho, se parece un poco a la sintaxis esotérica de aquí,
pero pronto veremos lo qué esto significa,
no obstante, es un poco más sencilla que la anterior
y la descifraremos.
El def aquí simplemente significa, define una función para mí,
mientras que en C, siempre especificamos
el formato de la función que debe regresar,
no lo haremos nunca más en Python.
Python todavía tiene tipos de datos, pero no vamos
a mencionar explícitamente los tipos de datos que usaremos.
Ahora bien, aquí está el nombre de la función
y main será una convención, pero no está
incorporada en el lenguaje de la misma manera como está en C, como veremos.
Por otra parte, este tonto maleficio, es solo
una forma de asegurar que la función predeterminada
se ejecutará en un programa de Python que, de hecho, se llamará main,
pero hablaremos más sobre eso cuando empecemos a crearlo.
Aunque, tal vez esta sea la diferencia más sutil pero también la más importante,
al menos desde el principio
y es difícil verla en esta escala.
Pero observen los dos puntos aquí y aquí, que ahora resalté en amarillo
y estos puntos, los cuales no deben escribirse,
pero están destinados a llamar nuestra atención hacia el hecho
de que presioné cuatro veces la barra espaciadora en esos lugares.
Si alguna vez obtuvimos algún comentario de nuestro TA o TF
de que podríamos tener un estilo mejor, más cercano al 5 de 5, debido a la falta
de sangría o de un formato más bonito, los de Python
nos ayudarán con esto.
El código de Python no se ejecutará si no inventamos las cosas correctamente.
Se fueron las llaves que encapsulan a las líneas de código relacionadas
dentro de algún bloque funcional
Y en su lugar, suelen reemplazarse con esta estructura general.
Tenemos dos puntos, luego debajo de eso y con sangría
están todas las líneas que tienen alguna relación, con esa primera línea de código
y las sangrías deben ser consistentes.
Y aunque a simple vista puede que no logremos
distinguir cuatro espacios de tres, el entorno de Python lo hará
y está implícito que esto nos ayudará
a aplicar un mejor estilo, acaso, de lo que podría
haberse hecho fácilmente desde el principio.
En Scratch, evidentemente tuvimos un bloque eterno,
el cual dice para siempre, hello, world y al igual que en C, nosotros
podíamos implementarlo de esta manera.
Ahora, existe un mapeo bastante limpio en Python,
ya sabemos que podemos deshacernos del punto y coma,
ya sabemos que podemos deshacernos de las llaves,
que tendremos que agregar dos puntos,
pero también podemos deshacernos de algunas cosas más.
¿Qué más falta para esta traducción de hello, world a Python?
Es algo más sutil.
De modo que podemos deshacernos definitivamente de las llaves
y confiar solamente en las sangrías.
OK, así que no hay paréntesis alrededor de while
y esto también pretende ser una característica de Python.
Si no necesitamos paréntesis para cumplir con el orden de las operaciones,
como, por ejemplo, en la aritmética y cosas similares entonces no los usaremos
porque solo son una distracción.
Únicamente son algo más para escribir,
y ahora el código está visualmente más limpio y es más fácil de leer.
Aquí también hay una pequeña diferencia,
True y False iniciarán con letras mayúsculas en Python,
pero eso es un detalle bastante incidental.
Sin embargo, observen que esto captura la esencia de Python,
no es un gran paso ir de uno a otro,
pero comenzamos a deshacernos de un poco
del desorden y de las cosas que nunca le agregaron mucho intelectualmente,
y era algo molesto tener que recordarlo desde el principio.
Aquí está nuestra variable booleana True
y ahora tenemos un número finito de iteraciones,
Tal vez queremos decir hello, world exactamente 50 veces,
en C, era un desastre cuando queriamos hacer esto,
tendríamos que inicializar una variable para llevar la cuenta,
pero sin incluir al 50, además agregar algo en el proceso, entre otras cosas.
En Python, esto será un poco más limpio,
después veremos lo que esto significa exactamente.
Pero si lo leemos de izquierda a derecha de algún modo dice lo que queremos.
¿Cierto?
For i in the range of 50,
entonces, es probable que i sea una variable.
Noten que no mencionamos su tipo,
el cual estará implícito de acuerdo al contexto, que en este caso
por el 50, aparentemente, tiene que ver con números.
Range por sí mismo, será un tipo de dato
que en ese sentido es un poco raro,
pero se le llama clase.
En esencia, esta es una característica especial de Python que, a diferencia de C,
donde, si queremos iterar un arreglo de valores o en 50 de estos valores,
en realidad tendríamos un arreglo con 50 valores
y lo genial de range es que se queda allí,
y cada vez que iteramos mediante un bucle, nos da el siguiente número
pero solo uno a la vez, por lo tanto usaremos tan poco como la
50ava parte de la memoria porque solo tenemos que mantener un número
al mismo tiempo
o tal vez un poco más que eso.
No es un ahorro tan perfecto,
pero esto solo es para i en el rango 50, lo que
de manera implícita será el contar desde 0 hasta 49.
Mientras tanto, lo que esté debajo será lo que imprimiremos esta vez.
Y aquí estaba uno de los enormes bloques de Scratch que teníamos al principio.
E i tiene una traducción bastante literal en el código en C,
Aún si nunca vimos Python antes,
tal vez podamos adivinar, cómo se vería el código de Python ahora.
Si esto a la derecha es el código en C, ¿cuáles
serán las características sintácticas que estamos a punto de eliminar?
Sí.
AUDIENCIA: Podemos deshacernos de las llaves y los paréntesis.
DAVID: Las llaves y los paréntesis desaparecerán,
¿Qué otra cosa podría desaparecer?
Los puntos y comas se irán,
al igual que las \n dentro de las declaraciones de impresión.
Genial.
Falta una cosa más, creo.
El if,
Entonces estrictamente no necesitamos los paréntesis,
porque no es como si combinara las cosas de manera lógica,
como esto o eso, o esto y aquello,
y esto debería ser suficiente como para deshacerse de esos dos.
Y aún hay un par de modificaciones que tendremos que hacer aquí,
pero, de hecho, el código será mucho más estricto, por así decirlo.
Esta vez, aquí, solo diremos lo que queramos.
Pero hay una cosa rara
y no es un error tipográfico.
¿Qué es lo que al parecer tendremos que saber desde ahora?
El elif y lo que sea.
Entonces, elif no es un error tipográfico.
De hecho, es la forma en la que expresamos la idea del else-if.
Pero por lo demás, todo es exactamente lo mismo.
También noten los dos puntos,
Sinceramente, es irónico, que si bien antes
pudo ser molesto que de vez en cuando olvidáramos un punto y coma,
ahora es posible que los dos puntos tomen ese papel,
pero en todo caso, todo lo que esté debajo de ellos debe tener sangría.
Aquí hay una diferencia fundamental, más allá de las ridículas diferencias
sintácticas entre esto y, digamos, otros lenguajes,
el flujo de trabajo que hemos usado hasta ahora, básicamente ha sido esto en C,
escribimos el código fuente en un archivo que, por lo general, termina en .c
ejecutamos un compilador, que para una verificación rápida se llama clang.
Técnicamente ya no es make,
make es solamente esta herramienta de construcción, que
vuelve automático el proceso de llamar a clang.
Así, estrictamente hablando, clang es el compilador,
además, clang genera los ceros y unos, también conocidos como código máquina.
Nuestra computadora Mac, PC o la que sea, tiene un CPU o unidad central
de procesamiento, fabricada por Intel o alguna otra compañía
y ese CPU está programado para comprender
ciertos patrones de bits, los ceros y unos también conocidos como código máquina.
De modo que, este ha sido nuestro mundo en C.
Con Python, umh, entonces el código que hayamos compilado en C,
por ejemplo, podría haber sido este: clang hello.c
el cual, como dijimos, ejecutamos con clang de este modo.
Pero si no especificamos el nombre de un archivo predeterminado como salida,
en su lugar, obtendremos en nuestro archivo todos estos ceros y unos,
los cuales después podrán ejecutarse por medio de ./a.out,
el nombre predeterminado para la salida del ensamblador, aquí.
Sin embargo, en Python, el mundo también se vuelve un poco más simple,
ahora que tenemos un código fuente y un intérprete,
pareciera que no hay un código máquina,
y tampoco un compilador,
además, sinceramente, hay una flecha menos, la cual
me sugiere que el proceso de ejecutar el código Python, por sí mismo,
será un poco más sencillo.
La ejecución del código en C, suele constar de dos pasos,
ejecutamos nuevamente clang, o por medio de make ejecutamos clang,
después ejecutamos el programa.
Y eso está bien,
no es del todo difícil,
pero son dos pasos.
¿Por qué no reducir los dos pasos, si lo podríamos hacer en uno?
Veremos lo qué eso significa exactamente.
en realidad, se trata de una simplificación excesiva.
En teoría, si queremos
ejecutar un programa como este, que simplemente imprime hello, world,
solo debemos ejecutar python hello.py.
y como resultado veremos hello, world en la pantalla,
como pronto lo comprobaremos.
Pero en el fondo, pasan otras cosas.
Aquí hay una especie de compilador,
pero no hay algo llamado código máquina como tal,
se llama byte code.
Incluso hay algo llamado máquina virtual de Python.
Pero todo esto es un poco abstracto para nosotros,
y sin duda para la conversación de hoy,
y también es así para el mundo real, en general.
Los humanos se han vuelto mejores a lo largo de los años
para escribir el software y las herramientas con las cuales podemos escribirlo,
de modo que, muchos de los procesos más manuales
y muchos de los detalles de bajo nivel en los que nos hemos
enfocado, aunque no sin esfuerzo, comienzan a desaparecer en C.
Al igual que en la semana 0, cuando poníamos una idea después de otra,
los ceros y unos, el ASCII, colores y todas esas cosas,
de modo similar, pero con las herramientas que tenemos, empezaremos a hacer lo mismo.
Entonces, lo que ocurre en realidad es
este proceso de aquí, en el que podemos pensar,
como algo bastante simple.
Ahora, si son curiosos y toman alguna clase de nivel superior
como CS61 o alguna otra, en realidad hablarán
sobre cosas como el byte code, el lenguaje ensamblador y todo eso.
Y vimos un destello de eso último, hace un momento.
Este es un lenguaje intermedio,
que el código fuente de Python convierte antes de ejecutarse por la computadora.
Pero nuevamente ignoraremos esos detalles de bajo nivel.
Aquí en nuestro kit, ahora están algunas de las herramientas.
En Python, hay tipos de datos aunque hasta ahora, no hemos
visto ningún ejemplo en el cual especifique los tipos de valores
que estarán en mis variables, o qué tipos de valores
devolverá una función.
Pero están ahí.
Todo acerca de lo que queremos que sea una variable
está escrito en términos generales, esto solo tomará ese tipo de datos,
así se trate de un entero, una cadena o algo similar,
la cadena no será una palabra completa,
en Python, esto literalmente se llama str.
Pero aquí hay algunos tipos conocidos bool, float, int y otros.
Y entre los demás, como lo veremos pronto, hay funciones como range,
pero antes de eso, noten que también daremos nuestra primera incursión
en Python con algunas funciones conocidas.
Python tiene mecanismos diferentes a C para obtener la entrada del usuario,
recopilamos algunos de estos detalles en una nueva biblioteca de CS50 para Python,
que en realidad solo usaremos algunas veces antes
de que hagamos la transición, pero encontraremos funciones como get_char,
get_float, get_int y get_string que controlarán toda la verificación de errores
que se requiere, al menos para nuestros primeros programas,
podemos comenzar con un auténtico trabajo, y terminarlo
sin sumergirnos en el fondo de esto.
Por último, aquí en nuestro kit hay algunas otras herramientas
Y hoy arañaremos la superficie de algunas de ellas.
Pero lo bueno de Python y más en general, de los lenguajes de alto nivel,
como los lenguajes más modernos que aprendieron lecciones de otros más viejos
como C,
es que obtenemos mucho más gratuitamente, mucho más fuera de la caja,
hay mucho más que un fregadero de cocina,
existen tantas metáforas que podemos usar aquí, las cuales hablan del hecho
de que Python, al igual que Java, tiene más características que C,
si estudiaron principios de la informática o algo más, que C.
Python tiene todo un kit de herramientas para representar números complejos,
diccionarios, que aparte de eso, se implementan como tablas hash
que ahora conocemos como lists, las cuales son sinónimo de un arreglo.
Pero un list es un arreglo que puede crecer y reducirse automáticamente,
no tendremos que saltar entre los aros como en C, a range que vimos brevemente
y el cual nos devuelve un número después de otro en algún rango, idealmente
por iteración,
set es un concepto matemático, donde
si queremos poner montones de cosas en una estructura de datos
y queremos asegurarnos de tener solo uno de cada cosa,
sin duplicados, podemos usar set.
Tuple también es un concepto matemático,
que nos permite combinar cosas que están relacionadas, sin comprometerlas
con las estructuras actuales.
Por ejemplo, x, y es un modelo muy común en muchos programas, gráficos,
videos y sin duda en las matemáticas y en la creación de gráficos en sí.
En realidad no necesitamos de una estructura de datos completa,
tal vez solo queramos decir x, y
y Python nos da ese tipo de expresividad.
Ahora vamos a sumergirnos con un mapeo rápido
de un mundo al otro y nos enfocaremos en lo que podemos hacer con Python,
de modo que aquí estoy, en el conocido IDE de CS50,
al igual que tenemos preinstalado a clang,
así como make y otras herramientas, también instalamos un programa.
Ese programa se llama Python el cual, a primera vista es un poco confuso
porque al parecer, Python es el nombre del lenguaje,
pero también es el nombre del programa.
Y aquí es donde Python es diferente.
Mientras que en C se compila nuevamente y usamos algo
como clang para convertirlo a código máquina,
Python es tanto el nombre del lenguaje como el nombre del programa que
usamos para interpretar el lenguaje.
De modo que lo preinstalamos en el IDE de CS50 y sinceramente fue en estos días,
y probablemente también en nuestras Mac o PC, incluso si no lo sabíamos,
hay un programa llamado Python que si le dimos su código fuente como entrada
hará lo que el código diga.
Continuemos y probemos algo como eso.
Permítanme seguir y guardaré un archivo a manera preventiva como hello.py
entonces .py será la nueva convención en lugar de .c
Para continuar, mantendré esto bastante simple.
Solo imprimiré la primera cosa mmm oh..
¡vamos memoria!
ya no es más printf,
es solo print ("hello, world")
Save, listo.
Este será mi primer programa en Python.
¿Por qué?
Esta es una línea de código
y es consistente con las características que he dicho que tiene Python.
Pero, ¿cómo lo ejecuto?
Bueno, en C, habríamos hecho algo como, make hello
pero make no sabe nada sobre esto, porque, por lo general, make se usa en C,
al menos en este contexto.
Entonces tal vez sea algo como ./hello.py.
No.
Parece que no tengo permiso allí,
pero hay un paso que adelanté antes en la diapositiva separada.
¿Qué hago para ejecutar un programa, dije?
PÚBLICO: Con Python hello.py.
DAVID: ¡Sí!
Debo ser un poco más explícito,
entonces python, que es el nombre del intérprete que entiende Python
y ahora necesito proporcionarle alguna entrada,
por el tiempo que llevamos en C, sabemos que los programas
pueden aceptar los argumentos de la línea de comandos
y de hecho, este programa lo hace por sí mismo, Python.
Solo le damos el nombre de un programa para que lo ejecute.
Y aquí lo tenemos, nuestro primer programa.
Así que todo eso está bien.
Pero ¿y si quisiera hacer algo un poco más interesante,
como obtener una cadena del usuario?
Bueno, en Python y especialmente en el IDE de CS50,
puedo hacer algo como esto: s = get_string( )
y puedo preguntarle a alguien por su nombre, así: s = get_string("name: ")
Ahora, el IDE de CS50 me está gritando
Undefined variable 'get_string'
veamos si tal vez solo tiene errores.
No.
Esto es un poco más esotérico que de costumbre,
Pero tenemos Traceback (most recent call last) :
File "hello.py," line 2, in cualquier cosa que esto sea,
Ahora, veo una línea de código en la línea 2, que dice
NameError: name 'get_string' is not defined
Esto no es el mismo lenguaje que vimos antes,
pero, ¿qué nos hace recordar?
Sí, como en el pasado, cuando nos olvidamos de cs50.h,
tuvimos algo sobre un identificador no declarado,
o algo como eso,
Esto simplemente no entendía algo relacionado con la biblioteca de CS50.
En C habríamos hecho #include
lo cual ya no es relevante porque ahora estamos en Python,
pero en esencia es algo similar.
Ahora diré from cs50 import get_string y lo guardaré,
con suerte desaparecerán temporalmente los errores cuando el IDE se dé cuenta,
¡oh, ahora importaron la biblioteca de CS50!
específicamente un método o función, más bien dentro de ella, llamada get_string
también tiene una sintaxis diferente, pero dice
lo que significa, de cs50, que al parecer es el nombre de la biblioteca,
importa una función llamada get_string.
Proseguiré y ejecutaré nuevamente python hello.py,
puedo seguir y escribiré en name: María
e ignorarla por completo porque necesito hacer una corrección aquí.
¿Cuál es el error obvio,
obvio ahora para mí, en el programa?
PÚBLICO: Necesitamos incluir la variable para s.
DAVID: Sí.
Así que necesito incluir a s, la cual tengo en la línea 3,
pero después ya no la usé de ninguna manera.
desde luego que esto va a salir mal, porque literalmente
dirá hello s.
Así es como solíamos hacer esto
y después pondríamos una s.
Pero este no es printf,
es print,
entonces la sintaxis es un poco diferente.
parece que podemos hacer esto en por lo menos dos maneras diferentes.
Tal vez la más fácil, si no es que la más obvia
sería algo como esto, donde simplemente podría decir print( "hello,
abrir y cerrar las llaves, ("hello, { }")
y dentro de eso, simplemente especificar el nombre de la variable {s}
que quiero introducir.
Pero eso no es lo único que debemos hacer allí,
lo ejecutaré una vez más.
Si escribo en name: María, oh,
aún no es del todo correcto.
Debo decirle a Python que se trata de un tipo especial de cadena,
es una cadena con un formato similar al que esperaba printf.
y la manera de hacerlo, aunque es un poco diferente de C,
es decir f: print(f"hello, {s}")
esta es una cadena f,
Antes de las comillas, escribimos la letra f.
Y si se ejecuta este programa,
se verá el nombre María como: hello, María.
Más tarde, me ocuparé de esa X roja.
Esa es una cadena de formato,
pero se puede hacer de otra manera,
aunque podría decir que no es tan obvia.
En la documentación en línea también podemos ver algo como esto.
Separémonos de esto por un segundo.
Lo que resalté en verde en Python
se conoce como cadena, y de otra manera como str.
str es el nombre de este tipo de datos.
A diferencia de C, donde una cadena es una mentira piadosa, donde
a fin de cuentas solo era un puntero, en Python
una cadena es un objeto first-class, lo que significa que
no solo son una secuencia de caracteres,
tienen funcionalidad incorporada, funciones incorporadas.
Al igual que las estructuras en C, que tenían muchas cosas dentro de ellas,
una cadena en Python tiene varias cosas dentro de ella,
no solo la secuencia de caracteres, sino funciones que pueden hacer cosas.
Accedemos a esas funciones por medio del mismo punto
operador como en C. Solo al conocer la documentación
o los ejemplos de las funciones que hay en las clases, los cuales estén dentro de
los objetos de la cadena.
Uno de ellos es el formato,
que es solo una función que se adapta a un argumento. ¿Qué
queremos insertar en una cadena a la izquierda del punto?
Pues con simplemente especificarle, oye, Python,
aquí hay una cadena con un marcador de posición,
dentro de esta cadena hay una funcionalidad incorporada, también conocida
como un método, cuando una función está dentro de algún objeto o estructura
pasa el valor s.
Pero si vuelvo a ejecutarlo después de guardar mis cambios,
debería ver que el nombre de María todavía está insertado.
¡Y eso es todo!
Pero es una idea sencilla de que ahora incluso las cadenas tienen
cosas adentro de ellas además de caracteres individuales
a las cuales podemos acceder por medio de los puntos.
Ahora nos extenderemos con un ejemplo que conocemos desde hace
tiempo.
De modo que abriré dos ventanas, una al lado de la otra
y veré si nos permite trasladar una hacia la otra
abriré, por ejemplo, el int.c de antes
recordemos a este programa int.c, cuyo único propósito
era obtener un número entero del usuario e insertarlo en printf
para después imprimirlo.
¿Qué será diferente en Python?
Bien, en Python, si implemento esto como, digamos, int.py,
luego haré lo siguiente,
me desplazaré hacia abajo para alinear un poco las cosas,
escribiré def ¡ups! main, como lo vimos en las diapositivas anteriores
y luego por aquí, hago i = get_int abro y cierro las comillas, integer,
aquí abajo, no diré printf ahora es print, abro y cierro las comillas,
"hello" y después el marcador de posición,
según nuestro ejemplo anterior, ¿cuál es la forma más sencilla de hacer esto?
con las llaves e i.
Ahora, quiero ser muy claro en que esta es una cadena f especial, o una cadena
de formato, en la cual podemos insertar los valores.
Guardaré eso.
Ya que tengo la mayor parte de las piezas juntas, y si por ahora
ignoramos la X roja, ¿qué otra cosa tenemos pendiente?
Cometí el mismo error que antes.
Sí, el get_int, de modo que aquí arriba es el que tenemos en la línea 3,
esta vez sería: from cs50 import get_int
Lo guardamos.
Y si en mi ventana de terminal ejecuto python de int.py
¡hum!
eso se ve extraño,
no es un error por una salida incorrecta,
simplemente no pasó nada.
¿Por qué ocurrió esto?
¿Cómo lo podemos solucionar, incluso si sabemos muy poco de
Python?
¿Eso fue una mano, o no?
¿No?
OK.
¿Sí?
PÚBLICO: ¿Hay un salto de línea?
DAVID: ¿Hay un salto de línea?
Eso está bien.
Solo trataba de que todo estuviera alineado,
pero ese no es el problema,
todas las sangrías son correctas, lo cual es importante para la estética.
Sí.
PÚBLICO: No llamamos a la función.
DAVID: No llamamos a la función.
Y aquí es donde Python es un poco diferente de C, pues recordemos que en C
main se llama de manera automática para nosotros.
Hace muchos años, los humanos decidieron que ese sería el nombre de una función.
Sobre la línea 6, en Python, el llamar de algún modo a main solo es una convención
pues podría haberla llamado foo, bar o con cualquier otra palabra
ya que no tiene ningún significado en específico.
Si queremos llamar a main en Python
tenemos que hacer algo como eso.
Creo que desde el principio es una de las distracciones más tontas.
Pero tenemos que decir esto:
if __name__ == "__main__":
main( )
Para resumir, cuando ejecutamos el intérprete de Python en un archivo,
como lo hicimos con Python, espacio, int.py o hello.py,
hay una variable global especial que nuestro programa puede llamar:
__name__.
Si ese nombre predeterminado pasa a ser __main__,
por ende, sabemos que podemos llamar a cualquier función que deseemos
de manera predeterminada.
Así que, de la misma manera como lo hicimos en la primera semana,
donde pasamos por alto ciertos detalles que no era tan interesantes.
Las líneas 11 y 12, por ahora, no las consideraremos interesantes,
pero, así iniciaremos estos programas.
Ahora, si ejecuto: python int.py, y escribo un gran número
obtengo: hello, 42.
Ese es el sentido de la vida, el universo, y de todo.
Hagamos algo más poderoso
que obtener un único entero del usuario.
Para continuar, cerraré este y cerraré este otro
y abriré ints.c, después dividiré mi pantalla en dos ventanas,
y abramos ints.c.
Este fue un poco diferente porque hicimos algo de aritmética,
aquí habrá otra diferencia con Python.
Esto es lo que hicimos en C, ¿qué fue lo curioso o lo que vale la pena
mencionar de las matemáticas en C?
¿Cuál de estos no se comportó como esperaríamos en el mundo real?
¿La división?
Sí, ¿por qué?
¿Qué hizo la división?
Si, disminuyó, redondeó,
o eliminó el valor al desaparecer todo después del punto decimal.
De modo que, en la línea 18, donde tal y tal, está dividida por tal y tal,
es tal y tal.
Y solo dijimos x dividido por y.
Si por ejemplo, en la primaria dividimos 1 entre 2, con suerte,
nos daría el valor de 1/2 o 0.5.
En vez de eso, ¿qué valor se calcula en C?
PÚBLICO: El cero.
DAVID: El cero.
En C se calcula truncado a un entero, al entero más cercano
sin el punto decimal que sería 0, porque 0.5 realmente es 0.5,
es por eso que tuvimos ese efecto.
En Python, las cosas serán similares
como un tipo de función o un error que se arregló.
En Python, a continuación, abriré un ejemplo
Escribí ints.py, por adelantado, que es
algo que lucirá así.
El equivalente de Python, que está más o menos alineado,
luce un poco diferente.
Hay algunas distracciones porque están todas esas cadenas f
en el camino.
Prestemos atención a como se insertan las x y las y.
¿Aparentemente es una nueva función aritmética en Python?
Se eliminó la división.
Este era el término más apropiado para lo que C hizo todo este tiempo.
Cuando usamos la diagonal en C, dividimos un número entre otro,
se divide y luego se redondea al entero más cercano.
Si queremos la misma función de la vieja escuela en Python
usaremos el símbolo //, que no se debe confundir con el comentario en C.
Si queremos que una división funcione como en la primaria,
se usará solo la diagonal.
Son cuestiones de menor importancia, pero que debemos tener en cuenta.
Si ejecutamos esto en Python, iremos a source 8,
después al directorio de las semanas, a la week/1 y ejecutamos: python ints.py,
ahora veremos x: 1 y y: 2.
Aquí están todos los valores que esperaríamos ver.
Muy bien.
No nos detendremos mucho y avanzaremos hacia algo
más poderoso como las condiciones.
Si queremos hacer algo solo de manera condicional en Python,
se expondrá en mi navegador como esto. Para continuar, diré:
conditions.py.
Lo siento, conditions.c que alguna vez lució así.
En este ejemplo, consideren que tenemos
un programa que obtiene dos enteros del usuario,
después compara dos veces a x y y, y al final
imprime si son mayores, menores o iguales que.
Aquí en la derecha, haremos esto desde cero.
A continuación, guardaré esto como conditions.py.
En la parte superior, ¿qué es lo que aparentemente
necesito primero?
Sí, la biblioteca de CS50.
Lo que queremos esta vez es: from cs50 import get_int.
Ahora, ¿cómo obtengo un entero?
O, ¿cuál sería la traducción de la línea 9 desde el lado izquierdo
al derecho de la pantalla?
x = get_int("x: ")
OK, ¿qué sigue después, si lo alineo más o menos aquí?
y = get_int("y: ")
¿Y que hay aquí abajo?
La condición
Pero, ¿si x es menor que y?
No son necesarios los paréntesis,
no está mal ponerlos pero es innecesario,
y ahora introduce una palabra en nuestra terminología
que no es de Python, por así decirlo.
Si no los necesitamos, no los pondremos.
Si x es, de hecho, menor que y, ¿qué queremos hacer?
Queremos imprimir x es menor que y, print("x is less tan y \ n"), ¿sí?
No.
OK.
Muy bien, bien.
Entonces, else if x > y:
OK, bien.
Cierto.
Parece tonto el elif, luego imprimamos print('x is greater than y').
Y como nota aparte, eso lo hice accidentalmente,
pero en Python podemos usar comillas dobles o también comillas simples,
ambas están bien, pero en C, las comillas simples tienen un significado específico,
¿qué significan?
PÚBLICO: Un carácter.
DAVID: Un carácter.
Son para caracteres individuales
y las comillas dobles significan cadenas, secuencias
de caracteres, lo cual implica a cero o más caracteres seguidos por \ 0.
En Python, todo eso se ha ido,
las comillas simples y las dobles son equivalentes.
Casi siempre uso comillas dobles por consistencia, como deberían hacerlo,
para mantener la consistencia dentro de sus propios archivos,
pero algunas veces es útil usar uno u otro tipo si anidamos,
por ejemplo, comillas, como tú podría tener de vez hace algún tiempo en C
OK.
Por último, else: print("x is equal to y")
De este modo queda más limpio
y en realidad no necesito todo este espacio en blanco,
así que reduzcámoslo un poco más
y así tendremos 11 líneas, cuando antes teníamos casi 27.
Pero para ser sinceros, omití algo.
¿Qué fue lo que omití?
Sí, no hice todo eso de llamar a la función
y tampoco mencioné a main,
aunque en Python esto no es estrictamente necesario.
Si vamos a interpretar un archivo que contiene un código de Python
y es un programa tan simple que no requiere que factoricemos el código
y lo organicemos en funciones separadas, entonces no lo haremos
Si esto sería lo que llamamos un script de la línea de comandos,
un programa que tiene líneas de código que solo podemos ejecutar
desde esa línea,
por ello, si voy a este directorio y ejecuto python conditions.py, Enter.
x será 1, y será 2
escribimos que: x is less than y
y eso es todo,
como lo dije antes, no necesito molestarme en hacer todo esto,
escribo, def main( ): y luego podría ir aquí,
pero si no sabían de esto y lo útil que es,
especialmente, para Python, podemos resaltar las líneas
y tabularlas todas a la vez.
Podría hacerlo así, pero necesitaría esto, lo cual probablemente
tendríamos que buscar cómo recordarlo si es la primera vez,
En este caso, no hay ningún valor para hacerlo,
pero puede estar allí, en caso de que se necesite.
A continuación, borraré eso
y volveremos a importar de uno al otro.
Muy bien,
de modo que esas podrían ser las condiciones
y veamos si no podemos...
ah, el noswitch allí.
Echémosle un vistazo a este,
déjenme abrirlo y en vez de compararlos a todos lado a lado,
ahora abriré esto que se llama noswitch.py,
el cual nos recordará a un programa que ejecutamos hace poco, el noswitch.c
Y tal vez podamos deducir lo que hace esto a partir de los comentarios.
¿Qué es lo que hace este programa en Inglés?
Porque el #Logical operators en la parte superior, no es tan explícito.
¿Qué es eso?
Sí.
Si alguna vez interactuamos con un programa que nos solicitó una línea
de tipo sí o no, aquí hay un código con el cual podríamos implementarlo
y lo podríamos hacer en C. Aquí solo comparamos los caracteres,
Pero existen algunas diferencias si pensamos
en cómo podríamos implementar esto en C,
incluso si no recordamos este programa en específico.
De este modo, importo mi biblioteca justo aquí
y esta vez llamo a get_char, la cual
también se encuentra en la biblioteca de CS50 para Python.
Luego observen que solo hay un par de cosas
sintácticamente diferentes aquí abajo,
además de los dos puntos, la sangría y demás, ¿qué otra cosa notamos?
Sí.
Gracias al cielo, ahora podemos decir lo que queremos,
si queremos hacer una cosa u otra, literalmente decimos "or"
y si en lugar de eso tratáramos,
aunque aquí no tenga sentido, de hacer la conjunción entre dos cosas,
esto y eso, literalmente podríamos decir "and",
en lugar de las dos barras verticales o los dos símbolos ampersand,
esta es otra leve diferencia con Python.
Ahora veamos otro ejemplo que nos recuerda a los anteriores,
este se llama return.py
Este es un ejemplo, más
compatible para tener una función main, porque ahora
comenzaré por organizar mi código en diferentes funciones.
Así que aquí estamos importando la función get_int desde la biblioteca CS50.
Aquí tengo mi función principal que simplemente dice x = get_int
y luego imprime el cuadrado de x.
¿Cómo van a definir su propia función personalizada
en Python que no sea solo main?
En la línea 11 es cómo se definiría una función llamada square,
que aparentemente toma un argumento llamado n,
podría llamarlo de cualquier manera, luego dos puntos, return n**2.
Aquí tenemos algunas características nuevas.
Pero no es la gran cosa, una vez que miran estas características
en un manual o en un curso.
¿Qué está haciendo ** probablemente?
PÚBLICO: Raíz cuadrada.
DAVID: No, no es una raíz cuadrada.
La potencia de... sí.
Así que n**2 es solo n elevado a la potencia de 2.
Esa no es una característica que tuviéramos en C. Pero que sí tenemos en Python.
¿y qué es esta línea 12 en verde que usa esas raras comillas dobles?
Sí, es un comentario.
Y es un tipo de comentario distinto de los que hemos
visto antes porque en mi ejemplo anterior, tuve algunos comentarios.
Recuerden que hace un momento, en conditions.py,
teníamos muchos comentarios.
Pide al usuario una x.
Pide al usuario una y.
Compara x y y.
Mientras que en C usábamos la diagonal doble,
Python, desafortunadamente la usa para la división de piso, por así decirlo.
Así que en su lugar solo usamos el hashtag o el signo de libra
para especificar una línea que debería ser vista como un comentario.
Pero aquí hay algo un poco diferente
Y no pensaremos mucho en ello por el momento.
Python tiene diferentes tipos de comentarios, uno de los cuales es este.
Técnicamente se le llama docstring o cadena de documentación.
Y lo bueno de Python, al igual que en lenguajes como Java y otros,
es que pueden poner comentarios en su código
que los programas especiales pueden leer y luego generan documentación para uno.
Si alguna vez estudiaron AP CS y vieron un Javadoc,
esta era una forma de comentar sus métodos
y su código en Java utilizaba signos raros como @ y otra sintaxis,
así, si ejecutaban un comando especial, generaba un manual de usuario para todas
sus funciones y le decía a sus colegas, amigos o profesores
exactamente cuáles eran todas sus funciones, cuáles eran sus argumentos,
cuáles eran sus valores de retorno y todo eso.
Del mismo modo, en Python pueden usar esas extrañas comillas, comillas, comillas,
docstrings para documentar su función.
Mientras que en C, nuestro estilo ha sido poner comillas arriba de las funciones,
en Python las ponemos como la primera línea que está adentro
y con sangría al interior de la función.
Muy bien.
Vamos a tratar de importar otra vez un programa desde el código,
recordando la primera semana en C cuando vimos este programa.
Así que hay bastante...
Uy, un spoiler.
No miren eso.
Afortunadamente no lo arruinó.
Este programa en C, lo vimos en la primera semana, get_positive_int.
Y su propósito era escribir un programa que
obtuviera en sí mismo un entero positivo del usuario,
no era tan interesante.
Pero fue una oportunidad para presentar algunas cosas.
Primero presentamos esta línea 6 hace varias semanas,
la cual se conoce como prototipo.
¿Y cuál era el propósito de tener ese prototipo de función allá arriba?
Sí, declaran la función, pero, ¿por qué?
Porque ya está implementado aquí abajo en la línea 15.
PÚBLICO: la forma en que se ejecuta el programa tiene que estar en orden
o algo así.
DAVID: Sí.
Por la forma en que se ejecuta el programa y, francamente,
debido a qué tipo de cosa ingenua o tonta que el clang es por diseño,
no sabe que una función existe hasta que realmente la ve.
El problema es que si en C tenemos main, dentro del cual
debe funcionar como get_positive_int,
pero no se implementa hasta algunas líneas después,
clang será muy tonto y simplemente no sabrá que existe.
Y no va a compilar su código.
Así, este prototipo, como lo llamamos, es una especie de señuelo, una pista que
no implementa toda la función.
Simplemente muestra el compilador con su tipo de retorno, sus tipos
y el orden de los parámetros, de modo que haya suficiente información
para luego solo confiar en que si simplemente compilo main a ciegas,
eventualmente veré la implementación de la función.
Entonces puedo compilar sus bits también.
Por ello, en C llamamos a get_positive_int,
lo pusimos en una línea,
lo almacenamos en una variable llamada i y luego lo imprimimos.
Y luego, para implementarlo usamos una construcción familiar
que ya hemos usado en otros programas.
Cada vez que quieran pedirle una entrada al usuario
y quieran seguir molestándolo
hasta que coopere con las condiciones que ustedes pongan,
usen el bucle llamado do-while.
Y recuerden, ¿por qué el ciclo do-while, es distinto del ciclo while?
PÚBLICO: Porque se ejecuta al menos una vez.
PÚBLICO: se ejecuta al menos una vez, lo cual
parece tener un sentido intuitivo si
queremos pedirle algo al usuario.
Y después, si el usuario no coopera, solo entonces
le volverán a hacer la solicitud.
En contraste con un ciclo while, esto va a suceder una y otra
y otra vez, sin importar qué, desde el primer momento.
Ahora veamos si no podemos convertirlo o importarlo,
por decirlo así, a Python.
A continuación guardaré un nuevo archivo llamado positive.py
y luego haré lo mismo que hice antes en main,
entonces pondré, digamos, from cs50 import get_int,
porque lo necesito.
Luego voy a aplicar mi método main aquí
Y luego dentro de main, al igual que en el lado izquierdo,
Voy a escribir i = get_positive_int...
("positive integer, please: ");
Se va a salir un poco del recuadro,
está bien.
A continuación imprimiré, recuerden,
tan solo una cadena f donde el marcador de posición sea i,
aunque, francamente, es un poco tonto
crear una cadena que no tenga otra cosa que el valor que queremos imprimir.
En Python es bastante bueno, solo impriman lo que quieran
y eso simplifica el argumento.
Ahora nos falta implementar, def get_positive_int(prompt)
el cual tomará una solicitud como su entrada.
Y no estoy especificando el tipo de datos de la solicitud, que es una cadena,
tampoco estoy especificando el tipo de retorno de esta función,
pero ambos existen por debajo del agua.
Antes, para obtener una variable ponía algo como esto,
int n; con punto y coma.
pero sé que no necesito el punto y coma.
Sé que no necesito el tipo de datos.
Y se ve algo tonto poner una variable para necesitarla.
En Python no se necesita hacer esto,
Si desean usar una variable, simplemente úsenla.
Desafortunadamente, mientras cualquier otra función que hayamos visto en Python
hasta ahora, se mapean directamente a una función en C,
Python no tiene un do-while.
Tiene el for-in y a while
y tal vez tiene otras cosas que no les he contado,
pero no tiene un do-while.
Sabiendo eso y lo que presentamos hasta ahora,
¿cómo podemos obtener un int del usuario,
asegurarnos de que sea positivo y pedirle uno sí y solo si
no lo es?
Dicho de otro modo, ¿cómo haríamos esto en C si quitamos
la construcción do-while?
¿Signos de admiración?
OK.
Tal vez podríamos invertir algo usándolo lógicamente.
PÚBLICO: Puedes utilizar un bucle while.
DAVID: Podríamos utilizar un bucle while.
¿Cómo?
PÚBLICO: Pondrías, while prompt es menor que 1.
DAVID: while prompt es...
OK, entonces el prompt es la cadena que vamos a mostrarle al usuario.
Entonces no sería prompt, creo.
Tal vez i o n, para ser consistentes con el otro lado.
¿Saben qué?
¿Por qué no... qué pasa con esto?
¿Qué pasa si... saben qué?
Sé que necesito un bucle.
Esta es, por mucho, la forma más fácil de obtener un bucle, ¿cierto?
Es infinito, lo cual no es bueno,
pero recuerden que puedo romper los bucles.
¿Qué pasa si hago algo como esto?
¿Qué pasa si pongo n = get_ get_int(prompt)?
Y luego, ¿qué quiero hacer?
Estoy dentro de un bucle infinito.
Así que esto va a seguir sucediendo, y sucediendo, y sucediendo hasta...
¿Es positivo?
Python no es tan fácil de usar.
No podemos decir solo eso.
¿Pero qué podemos decir?
PÚBLICO: Mayor que 1.
DAVID: Mayor que...
Cerca.
PÚBLICO: Igual a.
DAVID: OK, está bien.
Mayor que o igual a uno.
Y luego, ¿qué quiero hacer?
Salir.
Pero no es tan genial como un bucle do-while,
el cual nos da todos estas características, aunque francamente,
nunca fue tan bonito, ¿verdad?
En especial por el hecho de que tuvieron que lidiar con el problema del alcance
poniendo la variable afuera.
En Python, la forma correcta de hacerlo sería algo como esto.
Induzcan un bucle infinito, pero asegúrense de romperlo lógicamente
cuando sea apropiado hacerlo.
Y ahora, si agrego lo último que sigo necesitando.
Entonces, if --name-- == "__main__": y siempre halla algo para copiar y pegar,
llama a main ().
Ahora, en mi ventana de terminal voy a ejecutar python positive.py
y le pondré un 5 negativo.
¿Y qué tal un 1 negativo?
¿Y qué tal 0?
¡Ups!
¿Qué tal eso?
¿Qué tal 0?
¿1?
Hum.
Lo eché a perder.
Obtener nada es interesante.
Es algo así como nuestro nuevo nulo, por así decirlo.
Pero mientras en C el nulo puede, si se usa de manera incorrecta,
bloquear su programa, Python podría, al parecer, imprimirlo.
¿Dónde me equivoqué?
Sí, no hice que devolviera un valor real.
Y mientras clang podría haber notado algo como esto, Python,
el intérprete, no va a ser tan vigilante cuando se trata de averiguar
si al código le falta algo.
Porque después de todo, nunca dijimos que devolveríamos alguna cosa
y entonces no lo necesitamos estrictamente.
¿Qué podría hacer en vez de romperlo?
Podría devolver a n aquí
o podría hacer esto de modo equivalente, y asegurarme de que devuelvo a n aquí.
Y otra diferencia que hay en Python también, es el problema del alcance,
no es tan difícil como lo fue en C. Tan pronto haya declarado que n existe
aquí, ahora existe abajo.
A pesar de que fue declarado dentro de esta sangría,
no tiene alcance hacia ese bucle while solamente.
De cualquier forma podríamos hacer que esto funcione.
OK, intentemos ejecutarlo de nuevo.
Entero positivo.
1 negativo.
0.
1.
Y ahora vemos el número 1.
Muy bien.
Haré una pausa aquí, para ver si hay alguna pregunta.
¿No?
Sí.
PÚBLICO: ¿Se importan cosas desde la biblioteca CS50 de forma individual,
o se importa la biblioteca completa?
DAVID: Ah, buena pregunta.
¿Se importan cosas desde la biblioteca CS50 de forma individual,
o se importa la biblioteca completa?
Técnicamente pueden importar todo, de la siguiente manera.
Si quieren acceso a toda la biblioteca CS50,
pueden poner asterisco
Y asterisco en programación... bueno, en muchos contextos de computación,
el asterisco suele ser un carácter comodín
y significa todo lo que coincida con esta cadena.
Esto generalmente se considera una mala práctica
porque si el equipo de CS50 les da características o variables
que ustedes no quieren, todas esas funciones se habrán importado,
a su namespace, por así decirlo.
Por ejemplo, si la biblioteca CS50 tenía público adentro,
una variable llamada x, y y z,
además de funciones como get_string, get_int y get_char,
su programa tendrá ahora las variables x, y y z.
Y si tienen sus propias variables llamadas x, y y z,
taparán esas variables dentro de las nuestras
y rápidamente se volverá un desorden.
En general, deben ser un poco más quisquillosos
e importar solo lo que ustedes quieran.
Otra convención en Python es no especificarlo de esa manera
y en vez de ello importar CS50.
Esto no tiene el mismo efecto de importar
todas esas palabras clave como get_int y get_string
en el namespace de sus programas, como la lista de símbolos
que pueden escribir.
Pero entonces, lo que debemos hacer es
prefijar cualquier uso de las funciones en esa biblioteca
con el ahora familiar, o más familiar, operador punto.
Pero esta es solo una decisión estilística,
elegí conscientemente el otro enfoque
de modo que inicialmente, solo pueden llamar a get_int o get_string, tal como
lo hicimos en C. Pero técnica y tal vez más convencionalmente
la gente lo haría para dejar muy en claro que este no es su método get_int,
sino que es la función get_int de CS50.
OK.
¿Alguna otra pregunta?
Sí.
PÚBLICO: ¿Es una buena práctica de codificación poner el if __name__
o simplemente se debe ejecutar hello, world sin definir main.
¿Realmente se necesita hacer...
DAVID: Ah, buena pregunta.
La respuesta corta es, no.
Se los estoy mostrando de esta manera porque verán
ver esto en varios ejemplos en línea y en programas
que podrían ver que son de código abierto.
Estrictamente hablando, esto no es necesario.
Si al final crean su propia biblioteca, esto tiende a ser una característica útil,
Pero podría hacerse de manera equivalente, lo cual también está bien.
Aún puedo definir get_positive int.
Puedo deshacerme del main completamente.
Y ahora puedo hacer esto.
Por lo tanto, este programa es equivalente y está bien por ahora.
OK.
Una vez que aclaramos esto, veamos un par de ejemplos más
para ilustrar mejor las similitudes y diferencias
de algunas cosas.
Ahora abriremos, por ejemplo overflow.c, el cual vimos hace unas semanas,
y dividiremos de nuevo nuestras ventanas.
Y luego, en el lado derecho, abriré algo llamado overflow.py
que preparé de antemano.
Así que, a la izquierda tenemos un ejemplo de desbordamiento de enteros
en donde, si empiezo a contar en 1 y ni siquiera tengo una condición,
y sigo multiplicando i por 2, por 2, por 2,
y lo duplico, lo duplico, lo duplico y lo duplico,
sabemos desde C que suceden cosas malas si mantenemos
un incremento en algo sin ningún límite a la vista.
Este programa solo imprimirá cada uno de esos valores
y se pondrá en reposo durante un segundo en el inter.
El mismo programa en Python se ve bastante similar.
Pero noten que inicializo i en 1 y hace eternamente lo siguiente,
imprime i, multiplica i por 2 y luego se queda en reposo durante un segundo.
Pero estar en reposo no está integrado a Python en la forma en que funciona print.
Observen lo que tuve que incluir aquí
y no estaba seguro de lo que era.
Y sinceramente, hace unos días que busqué en Google,
"pausa de un segundo en Python", vi que estaba esta biblioteca,
en la cual está la función de reposo.
Y así es como cuál biblioteca debía incluir.
Y así como hay páginas con manuales para C,
hay todo un sitio web con documentación para Python
que también contiene toda esa información.
A continuación haremos lo siguiente.
Trataré de crear dos ventanas aquí.
¿Cuál sería la mejor forma de hacerlo?
Dividir una en dos.
OK.
Hagamos esto, para que pueda ejecutarlo en el mismo sitio.
Y si voy a mi directorio fuente...
[PITIDO DE ADVERTENCIA]
Por Dios.
A mi directorio fuente src8, voy a weeks/1y ejecuto make overflow...
no, lo siento.
Week/1...
OK.
Si voy a src1, ejecuto make overflow,
lo cual es muy lindo semánticamente, y ahora
podré ejecutar un programa llamado ./overflow.
Mientras tanto, dividiré esta ventana también.
Diablos, no allí.
Pongamos esto aquí.
¡Oh, no!
OK.
Un segundo,
perdón.
Overflow.py.
OK.
Así que ahora estamos... oh, ya perdí la otra ventana.
¡Oh! Eso es genial.
OK.
Hagamos esto.
OK.
Ahora ya sé cómo usar el IDE.
Muy bien.
En el lado izquierdo, estoy a punto de ejecutar overflow.
Y por último, sin que suene ese pitido de nuevo, voy a entrar aquí
y estoy a punto de ejecutar python of overflow.py.
Muy bien.
Y a la izquierda ejecutará la versión en C
y a la derecha ejecutará la versión de Python.
Y comenzaremos a ver...
Sin dobles sentidos, ¿qué pasa con estos programas?
Oh, diablos.
Tengo que desplazarlos.
OK, seguiré desplazándolos.
Esto es divertido.
OK.
La próxima vez busquen en Google cómo ponerlos en reposo durante medio segundo.
OK.
Entonces, aquí vamos.
Algo malo pasó aquí
y ahora C se está ahogando por completo.
Las cosas se pusieron horribles.
¿Qué pasó a la izquierda? Antes de que la respuesta se desplace.
Fue un desbordamiento de enteros, ¿cierto?
Tuvimos tantos bits convirtiéndose en uno solo, que finalmente
fueron confundidos temporalmente con un número negativo.
Y luego todo el asunto se volvió confuso
y se volvieron ceros permanentemente.
Mientras que en el lado derecho, en el de Python, ¡sí!
¡Miren como funciona!
Sigue contando cada vez más y más y más alto.
Y aún no hemos hablado sobre la representación subyacente
de estos tipos en Python, ¿qué podemos inferir
de la aparente mejor corrección en la versión de Python que está a la derecha?
No es una representación de ocho bits.
E incluso C, para ser más precisos, usa 32 bits para sus enteros.
Y así es como obtenemos cifras tan altas como 2 mil o 4 mil millones en total.
Pero es la misma idea.
¿Cuántos bits debe utilizar Python?
PÚBLICO: ¿64?
DAVID: Sí, tal vez 64.
No lo sé con exactitud.
Pero sé que no son 32 porque sigue contando y contando y contando.
Esta es otra característica de Python.
Mientras que los enteros en C típicamente son de 32 bits,
y aunque técnicamente eso es específico de la máquina,
los enteros en Python ahora son de 64,
lo cual significa que podemos hacer matemáticas a un nivel más alto y eso es
genial en aplicaciones que manejan datos científicos, estadísticas y demás,
donde tal vez tendríamos que lidiar con conjuntos enormes de datos.
Lamentablemente, aún tenemos algunos problemas de imprecisión.
Para continuar voy a cerrar este montón de ventanas
y abriré, por ejemplo, esta de aquí.
OK.
No, voy a omitir esto y haré algo
un poco más divertido, que es lo siguiente.
Entonces, en Python, haremos un calentamiento rápido.
Esto va a imprimir para mí, ¿qué?
PÚBLICO: Cuatro signos de interrogación.
DAVID: Cuatro signos de interrogación, ¿verdad?
Y esto es reminiscente, es como una versión realmente barata de "Super Mario
Bros".
Y si recuerdan la semana uno, donde exploramos esto,
había una captura de pantalla de "Super Mario Bros", en uno de los mundos
había cuatro signos de interrogación en los que Mario debía golpearse la cabeza
para ganar monedas.
Así que empezamos desde ese punto en C para hacer esto en su lugar.
Y ello nos dará otra característica.
Pero veamos si podemos inferir del contexto lo que hacen estos programas.
Aquí hay otro, Mario1.
¿Qué hace esto?
De seguro está utilizando un bucle.
¿Y cuántas iteraciones está usando aparentemente?
Cuatro.
De 0 a 1 a 2 a 3 en total.
Al parecer, cada vez imprimirá un signo de interrogación.
Pero ahora, solo infieran de esto.
Aún no he respondido a esta pregunta,
¿Qué más pasa en la línea 4 y por qué?
PÚBLICO: No va a una nueva línea.
DAVID: No va a una nueva línea. ¿Verdad?
Así que hay esta compensación en programación, y en CS más generalmente,
es como decir, hey, nos quitamos de encima la \ n que era tan molesta de escribir.
Pero ahora, si siempre está ahí, ¿cómo la desactivan?
Esta es una manera de hacerlo y también
revela otra característica fundamental de Python.
Noten que print aparentemente toma, en este caso, más de un argumento.
El primero es una cadena, print("?",...
El segundo es un poco diferente.
Es como la palabra fin.
Entonces es: end=" ").
Pero, ¿qué es esto de aquí?
Sucede que Python admite lo que llamamos parámetros nombrados.
Así que en C, cualquier parámetro que pasemos a una función
se define, básicamente, por el modo en que está ordenado.
Porque, incluso si una función toma argumentos que tienen nombres,
como x y y, o a y b, o lo que sea, cuando llamamos a la función,
no mencionamos esos nombres.
Sabemos que existen y así es cómo los vemos en la documentación
o en el código original.
Pero no nombramos a los argumentos cuando los pasamos y llamamos a una función,
en vez de ello los pasamos en el orden apropiado, según lo digan el manual
o la documentación.
En Python, de hecho, podemos ser un poco más flexibles.
Si una función toma varios argumentos y todos tienen nombres,
podemos mencionar los nombres explícitamente,
lo cual nos libera de los inconvenientes menores
de tener que recordar y acertar siempre el orden real de los argumentos.
Este caso, print toma al menos dos argumentos,
uno de los cuales se llama end.
Y si quieren usarlo, lo cual es claramente opcional porque no lo he
utilizado aún, pueden mencionarlo por su nombre, poner un signo igual
y luego especificar el valor que quieren pasar.
Si ahora entro aquí, voy a weeks/1 y escribo python
mario1.py, aún obtengo...
umh... en weeks/2.
Si pongo python mario1.py, aún obtengo cuatro signos de interrogación.
Pero ese es el resultado de imprimirlo con una línea que termina en comillas.
Mientras tanto, si hago esto, lo cual es un poco estúpido
porque voy a obtener igual si simplemente lo omito por completo.
Pero ahora tengo aquí cuatro signos de interrogación.
Y si realmente quieren ser originales, pueden hacer algo
como esto, que solo se utilizará, literalmente,
para darles eso en cambio.
Aplicar este enfoque tiene una utilidad incierta,
Pero eso es todo...
[PITIDO DE ADVERTENCIA]
Perdón... eso está pasando.
Echemos un vistazo a mario2.
Este funciona un poco diferente también.
¿Y cómo describirían la función que ofrece esta versión de Mario?
Imprime perfectamente cualquier cantidad de signos de interrogación.
Así que se parametriza al obtener primero un int del usuario que utiliza
la función get_int de CS50.
Y ahora estoy haciendo una iteración de i al rango de n, cualquiera que sea,
y luego imprimo los signos de interrogación.
Mientras tanto, mario3.py es un poco más elegante,
¿Pero qué estoy haciendo mejor esta vez?
PÚBLICO: Te aseguras de que n sea positiva.
DAVID: Sí, solo me aseguro de que la n sea positiva.
Así que no me molesté en implementar una función completa llamada
get_positive_int.
No la necesito.
Este es un programa súper corto.
Solo estoy usando la misma lógica aquí,
induzco deliberadamente un bucle infinito
y lo rompo solo cuando haya obtenido un entero positivo,
y luego imprimo muchos hashtags que nos recuerdan a los ladrillos
en Mario.
Y finalmente, tenemos esta versión un poco más sofisticada
que en realidad imprime de una forma completamente diferente.
Pueden inferirlo en los comentarios, pero concéntrese más en por qué.
La primera línea 12 itera de i a n,
sea cual sea la n que el usuario escribió.
Mientras tanto, la línea 15, con sangría, itera j desde 0 hasta n también.
Es algo así como nuestra convención para decir
int i = 0... , para int j = 0...,
en donde ya hemos tenido bucles anidados antes.
Noten que ahora que tenemos este bloque, el cual es una línea de código
o, conceptualmente, solo una pieza de Scratch.
Podemos anidar uno dentro del otro.
Aquí puedo imprimir un hashtag, asegurándome
de no poner una nueva línea después de cada hashtag que imprima,
solo imprimo una nueva línea en la línea 17, en cada iteración del bucle externo.
Y observen que, mientras en C lo hubiéramos hecho por costumbre,
lo cual está bien, en Python no necesitamos la f
y tampoco necesitamos la \ n end.
Por lo tanto, pueden simplemente imprimir y obtendrán, al menos,
una \ n automáticamente.
Así, cuando ahora ejecuto esta versión de Mario,
obtenemos algo más interesante.
Aumentaré el tamaño de mi ventana de terminal
para poder introducir un número positivo como este e imprimir 10.
Y ahora hemos obtenido un bloque completo.
Bueno, eso fue mucho.
Vamos a tomar nuestros cinco minutos de descanso
Y cuando regresemos, veremos algunos ejemplos aún más sofisticados.
Muy bien.
Vamos a iniciar la transición para resolver problemas con Python,
después de presentar un par de características adicionales
que no son características sintácticas sino reales del lenguaje.
A la izquierda está el viejo programa argv0.c que escribimos en la semana 3,
cuyo único propósito era permitirles
la ejecución de un argumento de línea de comandos por primera vez
y que era una linda herramienta para tener en nuestro kit de herramientas.
Pero, ¿cómo podríamos mapear esto?
Bueno, de hecho necesitamos saber que Python funciona
de un modo un poco diferente, como sigue.
Si abro un nuevo archivo llamado,
vamos a llamarlo argv0.py.
Voy a traducir esto, tal como lo hicimos antes.
Para ello, utilizaré lo siguiente.
If argc, no hay argc.
Así que, def main( ),
tampoco hubo argc o argv.
Y no es realmente correcto hacer esto y esto, como podrían suponer.
Las funciones de los argumentos de la línea de comandos
fueron proporcionadas por un paquete de Python, por así decirlo,
o una biblioteca, al igual que la biblioteca CS50 es un paquete
que pueden importar de Python.
Para ello, necesito hacer lo siguiente, import sys, que me da
acceso a un montón de cosas relacionadas con el sistema,
como lo que el usuario escribió en el símbolo del sistema.
Y si quiero controlar si el número de palabras que el humano escribió
en el símbolo del sistema es dos, de hecho,
haré esto: if len(sys.argv) ==2:
A continuación: print ("hello, ")
y un marcador de posición aquí.
Con los marcadores de posición, debo convertir esto en una cadena formateada,
será una cadena f.
Dentro de las llaves, puedo poner: print (f"hello, {sys.argv [1]}")
Entonces, es un poco diferente a lo anterior.
Tomé prestadas casi todas las mismas ideas que antes,
incluyendo cómo imprimir las cadenas.
A pesar, de que esto es un poco más detallado,
¿qué hay entre estas dos llaves?
Está el resultado de buscar en el paquete del sistema, que
tiene una variable llamada argv, por argumento del vector,
al igual que en C. Es en sí misma, es un arreglo, un alias de una lista en Python.
Aquí, tenemos el resultado de indexarlo como el elemento uno de esa lista.
Y la manera en que tengo acceso a esto es porque
importé ese paquete completo.
Si en el lado derecho, después de guardar ese archivo, hago:
python argv0.py, no veo nada. Si agrego
mi nombre: python argv0.py David veo: hello, David.
Es un programa muy similar, pero se implementó un poco diferente,
también notarán que la longitud de un arreglo,
conocido como una lista, no es algo que nosotros mismos
tengamos que recordar o tener cerca.
Podemos pedir el largo de una lista al llamar a len,
o L-E-N por length (longitud),
una función, que pasa como un argumento.
Ese es uno de los puntos de partida,
si queremos hacer algo un poco más inteligente,
como imprimir todas las cadenas en argv, como hace tiempo vimos,
podríamos recordar este ejemplo,
argv1.c, en donde tuve este bucle for.
Lo iteré desde de cero hasta argc, el conteo de argumentos,
imprimí cada uno de los argumentos en ese vector.
Python hace que incluso algo así sea aún más simple.
Para continuar, crearé un nuevo archivo aquí,
y lo llamaré, por ejemplo: argv1.py.
En Python puedo hacer: import sys
después: for s in sys.argv: print (s)
Lo hicimos.
De nuevo, solo dice lo que significa.
Importé la biblioteca del sistema.
sys.argv sé que es una lista, de argumentos de línea de comandos.
For algo en algo, es una nueva sintaxis que tenemos para bucles for.
Para alguna variable s dentro de esta lista, imprímelo.
Esta es una manera mucho más limpia, mucho más breve
de deshacerse de toda la complejidad,
simplemente al decir lo que queremos.
Mientras tanto, si quisiera imprimir cada carácter,
podría llevar esto un paso más allá.
Como hace tiempo en C, si quería imprimir
cada argumento de la línea de comando y cada carácter ahí podría hacer esto.
Solo necesito un par de bucles anidados, donde por medio del bucle externo,
itero todos los argumentos pasados.
Y en el bucle interno, itero sobre la longitud actual de la cadena
de cualquier argumento que imprimo.
Esto tuvo el efecto de imprimir todas las letras de los argumentos
de la línea de comandos a la vez.
Puedo hacer esto en Python, honestamente, es mucho más fácil.
Continuaré ahí.
Crearé un nuevo archivo llamado argv2.py,
e importaré sys, como lo hice:
import sys.
Después: for s in sys.argv: for c in s: print (c).
Lo hicimos.
¿Qué hace esto?
Ya se fue todo esa sobrecarga para for int i, for int j y lo demás.
En: for s in sys.argv, iteramos sobre todos los elementos de esa lista,
una cadena a la vez.
Para: for c in s, es un poco diferente ya que técnicamente
es una cadena o un objeto str, que es como lo llamaremos.
Pero a fin de cuentas, una cadena es solo una secuencia de caracteres.
Python soporta, desde el inicio, la habilidad de usar un bucle for
incluso para iterar sobre todos los caracteres en una cadena.
Por ello c, significa carácter:
for c in s:, me da cada uno de los caracteres.
A fin de cuentas, si ejecuto: python argv2.py,
sin nada, obtengo solo el nombre del programa,
porque eso es, desde luego, lo primero en argv, como en C. Y si
escribo, una palabra como María, obtengo: argv2.pyMaria,
en toda una columna larga, debido a las impresiones adicionales que suceden
y las nuevas líneas implícitas.
¿Alguna pregunta antes de proceder con el manejo de un paquete
llamado sys que usa estas funciones?
Muy bien.
Continuaré con algo ligeramente familiar.
Recordaremos initials.c de hace algún tiempo,
donde aceptamos como entrada en get_string el nombre de un usuario,
y luego imprimimos sus iniciales.
Para continuar, haremos algo como eso:
from CS50 import get_string
y diré, dame una cadena:
Quiero solicitarle al usuario su nombre, podríamos hacerlo aquí.
A continuación, diré: initials = " "
No sé lo cuales son todavía,
así que, inicializaré con una cadena vacía.
Después haré esto:
for c in s:, que es para cada carácter en el nombre de la persona, if
todavía no sé cómo decir esto:
If c is an uppercase letter: append c to initals
Y aquí abajo: print (initials)
Dejé un par de espacios en blanco,
por el momento, eso es solo pseudocódigo.
Pero esta línea 5, solo para ser claro, ¿qué es lo que hace?
¿Qué es lo que itera?
La cadena.
Para cada carácter de la cadena, para c in s, haré dos preguntas.
En C, lo hicimos de dos maneras diferentes.
Podemos hacerlo con controles de desigualdad
y considerar cuáles son los valores ASCII subyacentes.
La biblioteca ctype tenía la función isupper e islower que usamos.
Bien, resulta que c no es un carácter,
no existe tal cosa como un carácter en Python.
Solo tenemos cadenas de longitud 1.
Y las comillas simples ya no tienen ningún significado especial,
así que, c es técnicamente solo una cadena de un carácter.
Las cadenas son lo que comenzamos a llamar objetos,
que es un nombre más elegante para struct.
Su funcionalidad se encuentra dentro de un objeto, por ejemplo, una cadena.
Antes vimos una parte de la funcionalidad, ¿cuál fue?
No es la longitud, aunque esa otra.
Fue el formato.
Lo vimos brevemente,
pero cuando hice string.format, propuse
que en realidad hay una funcionalidad incorporada
a una cadena llamada formato.
¿Saben algo?
Existe un método o función dentro de la clase
de la cadena llamado isupper.
Y le puedo preguntar eso a la misma cadena que veo al decir:
if c issuper ( ): es verdadera, añada c a las iniciales.
En C, si las iniciales eran una cadena,
¿Cómo podríamos agregar otro carácter a una cadena?
PÚBLICO: ¿Con c.append?
DAVID: No en C. En el lenguaje C.
OK, entonces, ¿qué es una cadena en C?
Una cadena en C es una secuencia de caracteres,
en la cual la última es diagonal inversa cero (\ 0).
Muy bien.
Es un arreglo de caracteres, donde el último es una diagonal inversa cero (\ 0).
Si yo, por ejemplo, escribí mi primer nombre, "David",
y ahora quiero agregar "Malan" al final, ¿cómo hago eso en C?
PÚBLICO: [INAUDIBLE]
DAVID: Exactamente.
Es como una piedra en el zapato.
Crearíamos un nuevo arreglo, más grande que se ajuste a ambas palabras,
se copiaría David al nuevo arreglo, después el apellido, poner
el terminador nulo en el nuevo arreglo, luego liberar,
probablemente, la memoria original.
Quiero decir, es una cantidad ridícula de procedimientos a seguir,
y en algunas ocasiones lo hicimos, como tal vez en
la serie de problemas cinco.
Pero Dios mío, ya pasamos por eso.
Para continuar, añadiremos al arreglo el carácter que nos importa.
En este caso, no es un arreglo, sino una lista.
Perdón, no es un arreglo, es una cadena objeto, que al inicio estuvo en blanco.
Python admite esta sintaxis,
+= en aritmética generalmente significan sumarle un número a otro,
pero también significan añadir.
De modo que, podemos añadir a las iniciales al hacer += c, un
carácter adicional.
Aunque la cadena comience como esta y aumente en la memoria,
crecerá en un carácter,
crecerá, crecerá, crecerá, crecerá, hasta que tenga las iniciales de los usuarios.
Y de dónde viene ese recuerdo, ¿a quién le importa?
Este es el punto que ya pasamos,
Se lo dejamos al lenguaje.
Dejamos que la computadora comience a administrar esos detalles.
Si necesitamos llamar a malloc, está bien,
lo haremos.
No me molesten con ese detalle.
Podemos comenzar a pensar y escribir el código de clasificación
que conceptualmente está en este nivel, en lugar de este nivel.
De nuevo, abstraemos lo que es una cadena incluso
y al dejarla en el mismo lenguaje.
Si ahora ejecuto initials.py en Python, y escribo
por ejemplo, María Zlatkova, con M y Z en mayúscula,
Entonces, veo sus nombres porque le quité las iniciales de en medio.
Y si escribimos algo como David J. Malan, incluso con un punto,
infiere que las letras iniciales deben ir en mayúsculas.
De nuevo, es una manera mucho más estricta de hacer las cosas.
A continuación, abriré otro ejemplo,
que no vimos hace unas semanas, aunque se
incluyó en algunos de nuestros códigos de distribución, si lo desean buscar.
Hace algunas semanas, teníamos este programa entre el código de distribución,
donde declare un arreglo de cadenas llamada libro.
Y propuse que había varios nombres en la guía telefónica
por así decirlo, todos los instructores anteriores de CS50
ordenados alfabéticamente.
Y luego abajo en este programa de C, utilicé esa variable global llamada book
para implementar, lo que parece, una búsqueda lineal.
Y para implementar la búsqueda lineal en C, necesitaré
desde luego, un bucle para iterar sobre todas las cadenas.
Esta línea 26 hace exactamente eso.
Recuerdo que tuve que usar str compare en C,
porque que tropezamos con este problema
desde el principio, no pudimos comparar dos cadenas en C
porque compararíamos, accidentalmente,
sus ubicaciones, sus punteros, y no el valor real.
En este caso usamos str compare.
Y podía pasar el nombre que estoy buscando en el libro uno a
la vez, buscando la equivalencia de cero. if (strcmp (name, book[i]) == 0)
Puedo llamar a Mike, David o a quien sea
que intento llamar, o se detiene si no se encuentra al usuario.
¿Qué hizo este programa?
Si paso a este ejemplo, que una vez más, era de la semana 3,
y hago make linear,
no, no eso.
Otra vez, directorio equivocado.
Si entro en source3 y make linear, este programa
se supone que se comporta de la siguiente manera.
Para continuar, ejecuto ./linear, busco a nuestro viejo amigo Smith,
encontró a Smith.
Si continúo y busco, digamos a Jones, que no dio clases anteriormente
en CS50, dice parar.
Muy bien.
Mientras tanto, en Python, nos podemos deshacer de todo eso.
En nuestro directorio src8, y en nuestro subdirectorio 3,
a continuación, en su lugar abriré esto.
En Python, puedo declarar un arreglo, también conocido como lista,
casi de la misma manera.
Para ser claro, ¿qué es diferente?
PÚBLICO: ¿Los corchetes?
DAVID: Entonces, los corchetes ahora son corchetes cuadrados
en lugar de llaves.
A menos que hayan inicializado estáticamente un arreglo en C,
como codificar los valores para su arreglo en C,
es posible que ni siquiera supiéramos que podíamos usar llaves.
Eso no es un gran problema aquí.
En Python, los corchetes representan una lista de elementos,
literalmente.
¿Y qué más es diferente?
No declaré el tamaño del arreglo,
y técnicamente no tengo que hacer eso en C,
o bien, si codificamos todos los valores a la vez.
Hace falta algo en la línea 7.
PÚBLICO: El formato.
DAVID: ¿Perdón?
PÚBLICO: ¿El tipo?
DAVID: El tipo.
No especifiqué la cadena.
Esto es bastante similar a lo que hemos hecho en C.
Lo que es hermoso aquí,
para continuar, esconderé eso por solo un segundo.
A continuación, le pediré su nombre al usuario.
Pidamos su nombre aquí: name = get_string("Name: ")
Si quiero buscar book, que es solo una lista de nombres,
¿cómo implemento la búsqueda lineal?
Podría hacer: if name in book: print ("Calling {name}")
Hagamos que esto sea una cadena f.
Aquí abajo, eso es todo.
Así es como se implementa la búsqueda lineal en Python.
No necesitamos un bucle,
solo hacernos una pregunta nosotros mismos.
Si book es una lista, y name es la cadena que buscamos,
solo le pediremos al lenguaje que nos resuelva esto.
if name in book, es la sintaxis que podemos usar para formular esa pregunta.
Python, probablemente, usará, la búsqueda lineal sobre esa lista
porque no necesariamente sabe que está ordenada, incluso
aunque lo esté de manera alfabética.
Lo encontrará para nosotros. Y nos lo salvará
de mucha complejidad y tiempo para implementar eso nosotros mismos.
Mientras tanto, si quiero comparar dos cadenas,
propondré esto, escribiré un programa rápido aquí, compare1.py.
Continuaré con: from cs50 import get_string
Proseguiré con: s = get_string("s: ")
Obtendré otra cadena a la que llamaré t: t = get_string("t: ")
cómo lo hicimos unas semanas atrás.
En C esto tenía errores, ¿cierto?
If s == t: print ("same") else: print ("different")
Solo para ser muy claro, ¿Por qué es incorrecta esta idea general
de usar dos signos de igual en C?
Sí, están comparando ubicaciones, ¿cierto?
Esto fue como el día antes de que desprendiéramos
la capa de lo que realmente es una cadena.
Al parecer, si s y t en C fueron char * o ubicaciones,
significa que si obtenemos dos cadenas diferentes,
incluso si escribimos los mismos caracteres,
se compararán dos ubicaciones diferentes,
que no serán lo mismo.
Tal vez lo podamos inferir del tema de hoy,
¿Qué hará Python si se le pregunta si s y t son iguales?
Se responderá esa pregunta como lo esperaríamos de un humano.
En Python, el símbolo ==, comparará a s y a t,
mirará sus valores reales porque son cadenas,
y devolverá same si escribimos las mismas palabras.
Para continuar haré: python compare1.py, y escribiré,
por ejemplo, María, y después María, de hecho son: same.
Si escribo María y por ejemplo Stelios, son:
different, porque ahora compara las cadenas,
como lo esperamos hace algún tiempo.
Echemos un vistazo a otras cosas que
dieron lugar a algunos dilemas interesantes.
Podríamos recordar que en la semana cuatro, tuvimos este ejemplo en C,
noswap, llamado así porque esto simplemente no funcionó.
De manera lógica, aparentemente era correcto.
Swap no intercambió x y y, pero sí intercambió a y b.
¿Por qué?
PÚBLICO: ¿Las ubicaciones en la memoria?
DAVID: Las ubicaciones en la memoria fueron diferentes, ¿cierto?
Recordemos, x y y son variables en C que existen en un fragmento de memoria
que llamamos un marco del stack, el marco principal en el stack.
Por su parte, a y b tienen ubicaciones un poco diferentes en la memoria.
Un poco más arriba, mantenemos un orden similar
como si apiláramos una bandeja del comedor en el denominado stack.
a y b tenían los mismos valores que x y y,
1 y 2, pero con sus propias copias.
A pesar de que de manera lógica, con Kate, creo,
y los Gatorade, los dos valores se intercambiaron,
finalmente intercambiamos los dos valores incorrectos
sin cambiar, de manera permanente, los x y y originales.
Desafortunadamente, vaya, afortunadamente y desafortunadamente en Python,
no hay tal cosa como un puntero.
Esos desaparecieron,
y ya no tenemos la expresividad con la que,
de esa manera, resolvíamos este problema.
Pero, propondré que lo hagamos de una manera muy astuta.
Para continuar, declararé: x = 1, y = 2.
A continuación, imprimiré tanto como sea posible
con una cadena de formato: print (f"x is {x}, y is {y}"),
al introducir sus respectivos valores.
pondré esta línea dos veces.
En medio de ellas, intentaré realizar este intercambio.
Si sus mentes están listas para explotar,
podemos hacer eso en Python, hacer el viejo intercambio en Python.
Esto intercambia los dos valores como esperamos: x, y = y, x.
Este no es un caso muy común.
Y para ser justos, este es un ejemplo increíblemente forzado
porque si necesitábamos que se intercambiaran, bueno,
tal vez lo debimos hacer en primer lugar.
Pero si se habla de una de las funciones de Python,
podemos hacer algo como eso.
Les presentaré una característica adicional que de manera reciente
se adquirió en C. Esa es la noción de una estructura.
Para continuar, haré ese en código desde cero.
Guardaré este archivo de manera anticipada como struct0.py,
es una reminiscencia de uno de nuestros programas más antiguos.
Iré directo a hacer eso:
from cs50 import get_string
Después, me daré una lista vacía: students = [ ]
Esa sería una manera convencional de darnos
una lista vacía en Python.
Al igual que en C, podemos declarar un arreglo vacío,
pero, en C debemos saber el tamaño del mismo
de lo contrario, tendremos que usar un puntero,
o también tenemos a malloc.
No.
Todo eso desapareció.
Ahora en Python, ¿queremos una lista?
Solo diremos, necesitamos una lista.
que crecerá y se reducirá lo que se necesite.
Para continuar, lo haré tres veces, arbitrariamente:
for i in range [3], a continuación, preguntaré
un nombre al usuario que use get_string:
name = get_string("Name: ")
Dorm también usará get_string: dorm = get_string("Dorm: ")
Dorm está aquí.
Quiero añadir al arreglo a este estudiante.
Entonces, podría hacer algo como esto: students.append(name).
Resulta que... aún no hemos dicho esto.
Pero hay dentro del tipo de listas de datos un método
que es una función integrada llamada append, que añade caracteres.
Si tenemos una lista vacía,
y la llamamos por su nombre.append,
agregaremos algo al final de la lista.
Si no hay suficiente memoria para eso, no es problema.
Python nos encontrará la memoria, la asignará, moverá todo en ella,
y seguiremos nuestro camino sin preocuparnos por eso.
Pero, no se quiere almacenar solo el nombre,
se quiere guardar el nombre y el dormitorio,
entonces se podría hacer esto.
Podría ... bueno, tal vez no queremos estudiantes.
Tal vez algo como los dormitorios: dorms = [ ]
Y aquí podría hacer: dorms.append(dorm)
¿Por qué esto se convirtió en un mal diseño
si mi objetivo era asociar a un alumno con su dormitorio,
y después mantener esos valores juntos?
¿Por qué tener dos arreglos separados no es el mejor método
en Python o incluso en C?
PÚBLICO: ¿Puede ser la estructura?
DAVID: ¿Qué es eso?
PÚBLICO: ¿La estructura?
DAVID: Seguro, hay que mantener el doble de cosas.
Y, ¿qué más?
PÚBLICO: No podemos mapearlos uno con el otro.
DAVID: No podemos mapearlos el uno con el otro.
Eso es muy arbitrario.
Haré un tipo de contrato social,
supongamos que el alumno 0 vive en el dormitorio 0,
y el estudiante 1 vive en el dormitorio 1.
Eso está bien,
y puede ser cierto.
Pero, una de las funciones de las ciencias de la computación y de la programación
es la idea de encapsular, es decir, asociar memoria relacionada
con otra.
¿Qué hicimos en C?
No teníamos dos arreglos.
PÚBLICO: Tuvimos una estructura.
DAVID: Sí, tuvimos una estructura.
Así que, Python no tiene estructuras, per se.
En su lugar tiene algo llamado clases,
y algunas otras cosas como los tuples y namedtuples,
veremos más de eso en otro momento.
Al parecer, se puede implementar nuestra noción de estudiante,
y podría importarlo así.
La convención en Python es, si creamos nuestra propia estructura,
en lo sucesivo la llamaremos clase, y por convención iniciaremos su nombre
con mayúscula.
Es algo un poco diferente a las convenciones de C.
¿Cómo lucirá un estudiante?
Esta es, tal vez, la sintaxis más compleja que tendremos hoy,
solo tiene unas pocas líneas.
Si queremos implementar la noción de un estudiante, ¿cómo podríamos hacerlo?
En Python, decimos clase Student,
donde clase es similar a, solo para ser claro,
struct o typedef struct.
Pero en Python, solo decimos clase,
después esta es la parte rara.
Podemos declarar una función que por convención se deberá llamar init
por inicializar, y toma como primer argumento la palabra clave llamada
self, y otros argumentos como estos: def __init__(self, name, dorm):
Por razones que, con suerte, aclararé en un momento,
escribiré un código dentro de este método.
En resumen, ¿qué hice aquí?
Declaré un nuevo tipo de estructura de datos llamada Student,
y dentro de esta estructura de datos, de manera implícita,
hay dos cosas dentro de sí mismas,
algo llamado name y algo llamado dorm.
Así es como normalmente haríamos normalmente una estructura en C
con los tipos de datos y puntos y comas dentro de las llaves.
Mientras tanto, hay un método aquí.
Este método, en lo que cabe, está dentro de clase.
De lo contrario, es una función, con un nombre diferente.
Este método init toma lo que en sí mismo, hablaré más sobre eso en otro momento.
Pero, toma cero o más argumentos personalizados que podemos proporcionar,
y se llamaron nam y dorm.
Este método especial init
es una función que llamaremos de manera automática cada vez que se
crea el objeto Student.
¿Qué es lo que eso significa?
Eso significa que en nuestro código podemos hacer esto.
Se puede crear un alumno en la memoria al decir:
s = Student(name, dorm)
No tenemos esta función en C.
En el lado derecho, lo que se destacó
es el nombre de la clase y sus dos argumentos,
name y dorm, que son lo que el usuario exactamente escribió.
Lo que clase hace por mí, es que me asigna memoria
por debajo del agua para Student.
Tiene que ser lo bastante grande para name y para dorm,
así de grande, por así decirlo, en la memoria.
Y se ponen las cadenas name y dorm en ese objeto,
que devuelve al objeto completo.
Podemos pensar esto como una versión mucho más elegante de malloc,
que asigna toda la memoria que necesitamos.
Pero también se instala dentro de la memoria de name y dorm,
y lo agrupa, no solo dentro de un pedazo arbitrario de memoria,
en algo que podemos llamar el objeto Student.
Ahora, para nuestros estudiantes todo esto significa
que podemos continuar y anexar a ese estudiante a la lista.
Si más adelante quiero iterar: for student in students
A continuación imprimiré:
print (f"student name lives in {student.dorm"})
Y si ahora por aquí,
ups, se cierra eso.
Para continuar, ejecuto: python struct0.py,
¡Oh no!
Oh, gracias.
Eso va ahí,
Entonces ahora, diablos.
Llave perdida, gracias.
OK.
Para continuar, escribiré: María, Cabot, David, Mather,
Rob, y por ejemplo, Kirkland, ahora tenemos
los tres nombres.
También, hay otras maneras, si quisiéramos
de almacenar esto en disco.
Postergaré eso para un ejemplo en línea.
Veamos un ejemplo final, que con suerte
hará que nos arrepintamos de las últimas semanas o,
en cambio, aprovechemos las siguientes.
Recordemos eso,
aunque el primero, se supone, podría ser cierto incluso sin mi ayuda.
Si vamos al código de distribución, veremos este programa,
y no recorreremos todas sus líneas.
Este es un programa escrito en Python llamado speller.
¿Qué fue lo que me bloqueó con speller.c desde la serie de problemas 5?
Hace poco lo convertí de izquierda a derecha, desde C a Python,
y se implementó en Python de la manera más cercana a la que se pudo,
al usar las funciones de Python.
Solo con un vistazo, veremos que en apariencia mi implementación de speller
en Python hay una clase llamada Dictionary, la cual es muy similar a
dictionary.h en C. Notemos que todavía se tiene una constante aquí.
No es una constante, es una variable llamada length que es igual a 45.
Como lo hizo speller.c, también lo codifiqué en dictionaries/large.
Se usaron argumentos de línea de comandos, como lo vimos anteriormente,
pero en vez de C se implementó en Python.
Noten que podemos hacer cosas raras como esta,
que es una reminiscencia de nuestro truco de intercambio de hace un poco.
Si queremos declarar varias variables, todas en la misma línea
e inicializarlas, podemos enumerarlas con comas.
Entonces, del otro lado del signo igual,
enumeraré con comas los valores que deseamos asignar a esas variables.
Si nos desplazamos hacia abajo,
veremos en el código, sin tantos detalles, algunas frases familiares.
Este es el programa que ejecuta un diccionario para estudiantes
en alguna entrada, y después imprime todo esto en la parte inferior,
todas las frases familiares que podríamos recordar de la serie de problemas cinco.
Implementarlo en C tomó mucho trabajo. De manera comprensible,
posiblemente se usó una lista vinculada inicialmente,
o en última instancia, pudimos usar una tabla hash o tatar
o luchar con algo entre esas dos.
Eso es una función de C, es difícil,
es un desafío, porque tenemos que hacer todo por nuestra cuenta.
Una ventaja es que, en teoría, se obtiene un
gran rendimiento,
Una vez que se implementó el código, estaremos tan cerca del hardware
como sea posible.
Nuestro código se ejecuta bastante bien y depende
solo de nuestros algoritmos, no de nuestra elección de lenguaje.
Continuaré con la implementación de un archivo llamado dictionary.py.
Propondré esas palabras: class Dictionary:
el equivalente, lo siento, de dictionary.h sería este archivo.
Y tendrá una función llamada check,
que toma un argumento llamado word: def check(self, word):
Tendrá una función llamada load, que
toma un argumento que se llama dictionary: def load(self, dictionary):
Tendrá un método llamado size, que no toma otro argumento más que así mismo:
def size(self):
Tendrá un método llamado unload, que tampoco toma otro argumento que así mismo:
def unload(self):
Si les hubiéramos asignado la serie de problemas cinco en Python,
esencialmente, le habríamos dado un archivo llamado dictionary.py
con estos marcadores de posición ya que recuerdan a la serie de problemas cinco,
todos esos fueron para dos.
En sentido estricto, podría haber otro aquí.
Probablemente por cada clase en Python tendremos: def __init__(self):
porque cada clase en Python, normalmente tiene este método init,
para inicializar la estructura de datos: self.words = set( )
Para continuar, haré esto,
todavía no sabemos mucho de Python,
y damos por hecho que el deletreo funciona.
A continuación, cargaré algunas palabras en dictionary,
con mi método llamado load.
Dictionary es el nombre del diccionario que se cargará
ustedes mismos implementaron esto al cargar esos archivos desde el disco.
Haré lo siguiente en Python:
file = open(dictionary, "r")
for line in file:
Para continuar, agregaré:
self.word.add(line.rstrip("\ n"))
Continuaré con: file.close( ) return True
se terminó de implementar load.
Ese es el método load en Python.
¡Sí, estoy feliz!
OK.
Así que, verifiquemos,
verificar también es una lucha, ¿cierto?
Porque una vez que tengamos nuestra tabla hash, o una vez que tratemos
de navegar en esa estructura en la memoria,
recursivamente, o de manera repetitiva, al seguir muchos
de punteros y similares, al seguir una lista vinculada.
¿Qué tal si yo solo hago?
if word.lower( ) in self.words: return True
else: return False
Terminamos,
hemos terminado.
Size, podemos inferir cómo hacer esto:
return len(self.words)
¡Terminamos!
Unload, no nos preocuparemos por la memoria de Python, terminamos.
Ahí está, un problema de la serie cinco.
[APLAUSOS]
Gracias.
¿Cuáles son las conclusiones?
Sienten gran euforia de que ahora tienen este poder, o gran tristeza de que
primero implementaron eso en C. Pero esto
en última instancia, pretende ser temático.
Esperemos seguir adelante, aún si tuvimos problemas con cualquiera de estos
temas, listas vinculadas, tablas hash, punteros y similares,
con suerte tendremos una comprensión general
de algunos de estos fundamentos y lo qué las computadoras
hacen por debajo del agua.
Y ahora con lenguajes como Python y pronto con JavaScript, SQL,
con un poco de HTML y CSS mezclados para nuestras interfaces de usuario,
tendremos la capacidad de resolver problemas,
dando por hecho tanto su comprensión de esos temas
y la realidad de que alguien más implementó esos conceptos para nosotros
de modo que cuando se trate de resolver la serie de problemas seis, siete y ocho,
y después de dejar CS50 y resuelvan problemas de su propio dominio,
tengan muchas más herramientas en su kit.
El objetivo para ustedes será elegir
el que sea más apropiado.
Así que, ¡levantémonos de aquí!
Me quedaré para responder preguntas.
Nos vemos la próxima vez.
¡Que tengan la mejor de las suertes en el examen!