Aprendiendo Python mediante pequeñas pruebas

Josepanaero

Motivación
Desde hace bastante he querido ponerme a aprender python, pero realmente nunca me he puesto porque tampoco necesito hacer ningún programa específico con él. Por eso se me ha ocurrido que podríamos organizar una serie de pequeñas pruebas. Me explico: cada semana se irán proponiendo ejercicios para realizar en python, y los usuarios que quieran podrán poner sus soluciones aquí y compararlas con las del resto. La idea es que estas pruebas sean incrementales en cuanto a dificultad, para así ir cada vez aprendiendo aspectos más complicados de python.

La primera prueba la expondré yo, pero la idea es que entre todos vayamos proponiendo nuevos retos, los cuales se irán realizando una vez que se acabe el plazo de entrega de la prueba actual.

Prueba 1: Números Perfectos
Prueba 2: Solapamiento de dos tuplas / listas
Prueba 3: Números triangulares
Prueba 4: Clase de números romanos
Reglas
Recursos
bLaKnI

Python es sencillamente, PERFECTO. xD

Amén de que es facilisimo!
Por cierto, con Euclides es muy muy fácil. :)

SeRiaL_k

Cuadno empieces con python ya no volveras a los otros xD

erdanblo

La verdad es que yo también tenía ganas de ver python desde hace tiempo, a ver si le hecho un rato y me apunto a los ejercicios estos :P.

Fyn4r

Me gusta la idea, si tengo tiempo esta semana (malditos exámenes xD) me lo miraré.

B

Tengo hecho uno, lo cuelgo en un rato. Lo he hecho en unos minutos y la eficiencia da asco (le he puesto comprobar hasta 100.000 hace un rato y aún no ha acabado xD). Pero bueno, yo lo posteo igualmente, siempre entretiene aunque sea una chapuza.

EDIT: No seais duros, está hecho muy aprisa. Sé que hay comentarios estúpidos pero los he puesto más bien para mí, porque a penas sé python. Probablemente tanto las estructuras de control como las de datos se puedan manejar mejor, simplemente he optado por lo primero que he visto.

Por otra parte, no he esmerado en la E/S, así que si introduces un número inferior a 6 pues te saldrá [] en vez de "No hay resultados", o cosas similares.

código
código después de los consejos de #7

(#7, quizás sería mejor un append(i+1), pero tampoco controlo de como eficiente es cada cosa)

Aviso, ambos programas con números altos (>10000) tarda una burrada, poned un print(i) en getPerfectos para ver cómo va.

MaKi

#6 te doy algun consejillo que espero que no te lo tomes como ninguna crítica:

La función "esPerfecto" que devuelva 1 o 0 es grave xD, mejor return True o return False (ojo con las mayus).

De esa forma todo linea que tienes como:

if (esPerfecto(i+1)==1): 

Es sustituida simplemente por:

if (esPerfecto(i+1)): 

Otro cosa, en esta linea:

res = res + [i +1]

cambiala por :

res.append( i+1 )

ó

res.extend( i+1 )

Según lo que quieras hacer. Este punto es muy importante para la eficiencia.

También aclararte que print en python se suele usar simplemente con (sin paréntesis) :

print "hola mundo"

Como sabes mete automáticamente \n al final, si no lo quieres pon:

print "hola mundo",

Por otro lado la llamada al main(), mejor haz esto:

if __name__ == '__main__':
	main()

De esa forma si ese modulo lo quieres usar desde un "import" de otro modulo, no te ejecuta el main, pero para un ejemplo pequeño, no esta mal.

Por último, yo evitaría cualquier llamada recursiva y la sustituiría por un while con condición de salida.

Eso respecto a estilo de programación, en cuanto al algoritmo parece que esta bien, pero no lo he probado :D

Enga un saludo

B

#7: Thanks, la verdad es que de Python no tengo mucha idea, así que me ha salido así. Respecto a lo del true y false, ahora que dices que es case-sensitive sí, pero antes no lo sabía, así que fui directamente al 0 o 1. Luego lo de main y tal lo sabía por encima, pero como solo era para hacer el código ni me molesté (cierto, así poco se aprende). El caso es que lo he hecho de prisa. Y sí, me lo tomo como una crítica pero constructiva, y esas siempre son bienvenidas, gracias :)

EDIT: Arreglaré el código y lo pondré en #6, dejaré también la primera versión para que se entiendan las críticas, por si puede ayudar a alguien.

ElKedao

Joder no me imaginé que fuera tan parecido a C con toques de Pascal :|

bLaKnI

En verdad, este ejemlo es mucho mas fácil.
Para los que quieran, lo pueden encontrar resuelto en varios lenguajes (y python en cocnreto) en el siguiente sitio:

spoiler

Por cierto, Python no funciona mediante cierres por claves, sino que funciona por tabulaciones, lo que es divertido. xD

condicional:
     sentencia A
     sentencia B
          condicional:
               sentencia C
     sentencia D

A, B y D, son del primer condicional.
C del segundo.

B

#10: Sí, eso mola un huevo. Aunque al no estar acostumbrado a veces no sabes por qué te da fallo. Aunque por mucho que digan a nivel de sintaxis yo prefiero Java xD

EDIT: A ver si se anima más gente, que esto puede llegar a ser interesante.

Ga1a

#10 Gracias a eso en python siempre te queda un código bonito y tabulado, pero no veas la capullada que puede llegar a ser que en los ordenadores de la facultad al gedit se le vaya la pinza, tabule mal y te peguen los identations fault esos por todos lados xD

Por la noche igual me animo a hacer algo en plan sencillito, que lo único que di de python fue usando pyGTK :_

Josepanaero

Bueno, me alegra ver que ya se ha animado gente a poner códigos y comentar los de otros :) A ver si esto sigue un poco adelante, que a penas se habla de este lenguaje en MV...

Mi código para la prueba 1:

spoiler

A parte de esto, pues os pongo varias dudas que se me han ido ocurriendo:

  • ¿Cómo hago para que si en vez de meter un entero se mete un carácter, string, flotante, etc. la función "int" no me dé error?

  • ¿Cómo puedo hacer para que en la función range me llegue hasta el número que introduzco en el segundo parámetro y no hasta el anterior? ¿Siempre hay que sumarle 1 o hay una solución más elegante?

También sobre el código de #6, una pregunta: ¿para qué usas en la función getPerfectos la variable inc?

Y también comentar que he estado pensando que quizá en vez de poner cada semana una entrega, lo ideal sería ir poniendo una lista de pruebas y que la gente haga la que le interese cuando quiera, sin límites de nada, ¿no? ¿Qué os parece? Se aceptan sugerencias sobre pruebas xD De momento he añadido una segunda prueba, a ver si alguien más propone alguna prueba para hacer.

Un saludo!

BLZKZ

yo mañana me lo miro e intento algo, que he leido lo de c+pascal sin llaves y me ha entrado el gusanillo. (lo que pueda hacer en una hora :P que estoy de examenes ^^)

Siempre he querido aprender python.

MaKi

#13 mirate esto, debes capturar la excepcion, que en este caso es ValueError, además he aprovechado para explicar lo útil que son los valores por defecto.

Para capturar cualquier tipo de excepcion:

try:
... codigo ...
except:
... tratamiento excepcion ...

Una excepcion muy típica, es el CTRL + C

try:
... codigo ...
except KeyboardInterrupt:
print "Abortado"

#!/usr/bin/python

def pedirEntero( pregunta="Introduce un entero" , mensajeERROR="ERROR. No has introducido un entero" ):
	insistir = True
	while insistir:
		try:
			numero = int(raw_input ("%s : " % pregunta))
			insistir = False
		except ValueError:
			print mensajeERROR
	return numero
	
print "Has introducido el num = %d" % pedirEntero()
print "Has introducido el num = %d" % pedirEntero("OTRO PLZ")
print "Has introducido el num = %d" % pedirEntero("OTRO PLZ PLZ" , "K COJONES PONES")
Soleil

si hago las pruebas en Scheme, vale? : -P

MaKi

TIOBE es una empresa de de Ingeniería del software, tienen un ranking muy interesante, para tratar de calcular el mejor lenguaje:

http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html

"The popular search engines Google, MSN, Yahoo!, and YouTube are used to calculate the ratings. Observe that the TIOBE index is not about the best programming language or the language in which most lines of code have been written."

Esta basado en buscadores

En el 2007 python fue top 1, ahora mismo esta 6

scheme k es ? xDD he leido k es funcional ... en mi uni solo damos camel, es mejor ? k diferencia hay ?

Soleil

hola

Scheme es un lenguaje multiparadigma aunque principalmente funcional, dialecto de la familia Lisp, basado en listas y bastante minimalista en cuando a librerías. No creo que sea mejor o peor, sino diferente en muchos aspectos.

Echa un vistazo aquí:
http://es.wikipedia.org/wiki/Scheme

No me importaría participar en Python, aunque necesitaría un intérprete portable que pudiera usar fuera de casa, creo que además hacerlo en Scheme molaría, por aquello de comparar los resultados. : -)

Soleil

Bueno, en todo caso... ahí va mi solución al primero de los retos en Scheme...
Me encantan estos retos :-)

Lo he resuelto utilizando dos algoritmos.
El primero de ellos es un cálculo simple, parecido a los que se pusieron en Python
y al método de la Wikipedia que calcula cada divisor para los números, pero es sencillo.

;; Devuelve cierto o falso según un número es perfecto.
(define (perfect? num)
  (let ((count 0))
    (begin
      (for-each
        (lambda (n)
          (if (zero? (remainder num n))
          (set! count (+ count n)))) (cdr (iota num)))
      (if (= num count) #t #f))))

;; Lee un número N por teclado y filtra una lista construída 
;; con los números desde 1 hasta N según cada uno es perfecto o no.
(display (filter perfect? (cdr (iota (1+ (read))))))

Si al programa le pasamos un 500, mostrará:
(6 28 496)

El problema de este procedimiento es que es sumamente lento.
Si probais a pasarle 10000000 (10 millones), os llevará la vida hacer
el cálculo. (es también un problema en las soluciones con Python)

Leyendo por ahí (en la Wikipedia inglesa) encontré otro método
utilizando números primos y una fórmula tal que:
Si (2k − 1) es primo entonces 2k - 1 * (2k - 1) es un número perfecto
hecha por Ibn al-Haytham y Euler.
(leer aquí: http://en.wikipedia.org/wiki/Perfect_number)

Así que hice otro programilla. :-)

;; Devuelve #t si un número es primo
;; y #f si no lo es. Sólo comprueba impares
;; y va desde k 3, hasta k * k > n.
(define (is-prime? n)
  (if (even? n) #f
  (let loop ((k 3))
    (if (> (* k k) n) #t
    (if (zero? (remainder n k)) #f
    (loop (+ k 2)))))))

;; Construye una lista de números perfectos
;; desde 0 hasta k. Utiliza la fórmula anterior
;; Optimiza terminando en cuanto un resultado
;; es mayor que el número pedido. 
(define (perfect-numbers k)
  (let loop ((num 0) (ls '(0)))
    (if (> (car ls) k) 
      (cddr (reverse (cdr ls)))
    (if (is-prime? (- (expt 2 num) 1))
      (loop (1+ num) (cons (* (expt 2 (- num 1)) (- (expt 2 num) 1)) ls))
      (loop (1+ num) ls)))))

;; Ya podemos rular el programa.
(display (perfect-numbers (read))

Ahora el programa sí es eficiente.
Si le pasamos 100000000 (100 millones) devuelve
instantáneamente:
(6 28 496 8128 33550336)

o con 100000000000 (100 mil millones)
(6 28 496 8128 33550336 8589869056)

Saludos!

Soleil

Y aquí va la solución al segundo.

A mi parecer es mucho más difícil de lo que realmente parece tras
leer el enunciado. Razones:

  • Las listas pueden ser de tamaños diferentes
  • No tienen porqué contener números
  • Pueden contener elementos repetidos
  • No tienen porqué estar ordenadas

Al principio pensé en dadas las dos listas:
(10 20 30 40 50)
(30 40 50 10 20 40)

Coger el último elemento de la primera, buscar el primer coincidente
en la segunda e ir comparando hacia atrás. Por desgracia, ésto no es viable.
Motivo:
(10 20 30 50 50)
(30 50 50 20)

El primer elemento coincidente sería el primer 50, pero hay dos.

Tras darle unas vueltas, opté por otro tipo de solución...
Dadas las listas:
A = (10 20 30 50 50)
B = (30 50 50 20)

Crear dos funciones que recojan elementos por la derecha o por
la izquierda de cada lista, para poder hacer sublistas.

Comenzar entonces por la sublista mayor (de N elementos) de B por la izquierda y
compararla con la sublista de N elementos de A por la derecha. Continuar hasta
que la sublista de la lista más pequeña es la lista vacía o hasta que ambas
sublistas coinciden. Devolver el tamaño de la última sublista. : -)

;; Recoger una sublista por la izquierda de N elementos.
(define (take-left ls n)
  (if (zero? n) '()
  	(cons (car ls)
	    (take-left (cdr ls) (1- n)))))

;; Recoger una sublista por la derecha de N elementos.
(define (take-right ls n)
  (let loop ((num (- (length ls) n)) (ls ls))
    (if (> num 0) (loop (1- num) (cdr ls)) ls)))

;; Recorrer las posibles sublistas por la derecha de LS1
;; y por la izquierda de LS2, comparándolas para N número
;; de elementos, parando cuando son iguales. Comenzar
;; por la mayor sublista posible de la lista más pequeña
;; asegurando que siempre hay elementos. :-)
(define (list-overlap ls1 ls2)
  (let loop ((i (min (length ls1) (length ls2))))
    (if (equal? (take-right ls1 i)
                (take-left  ls2 i))
     i (loop (1- i)))))

Probando el programa:
=> (list-overlap '(10 20 30 50 50) '(30 50 50 20))
3

=> (list-overlap '(-9 20 "hola") '("hola" "hola" 40 91))
1

Espero no os moleste que esté resolviéndolos en Scheme y ponga tochos
tan largos de explicación y tal; me gusta aprender y me encantan este
tipo de retos. Este segundo problema es una idea genial!

Un saludo!

Josepanaero

Mi solución de la prueba 2: Solapamiento:

spoiler

He dejado dos soluciones, las dos hacen lo mismo, pero la segunda es un poco más clara.

Con los ejemplos que he puesto, podéis ver como esta función es, además de simple, muy potente, porque también permite comparar con listas dentro de listas, tuplas dentro de listas, etc.

#15, gracias, no se me había ocurrido usar excepciones.

Soleil, está genial que las hagas en Scheme. Yo no lo conocía, y siempre está bien comparar la misma solución escrita en otros lenguajes. De todas formas, si comparo tu solución con la mía, creo que en python queda más claro y además no he tenido que crear ninguna función adicional para recorrer listas hacia atrás. Por curiosidad, si en scheme metes una lista dentro de otra, ¿sigue funcionando bien tu programa?

También me alegra que te haya gustado el ejercicio del solapamiento. Se trata de una práctica de Metodología de la Programación, de 1º de Ingeniería Informática. Hace ya 6 años tuve que hacerla en C++ y ahora al hacerla en Python me he alegrado un montón, porque lo que en C++ me ocupaba un buen puñado de líneas, en Python se soluciona en SEIS líneas...

La verdad es que me está gustando mucho este Python, a primera vista parece que al darte tantas funcionalidades y facilidades, te ahorra mucho tiempo, porque puedes ponerte a programar directamente sin tener que estar implementando por tu cuenta cosas triviales y repetitivas, no sé si me explico.

Ah, si se te ocurre algún ejercicio, por favor, postéalo. Ahora voy a pensar en algún ejercicio relacionado con clases, para practicar un poco con ellas. En cuanto se me ocurra algo lo pongo.

EDIT: Sobre el método de Euclides, también pensé en hacerlo así, pero me surgió una duda. Está claro que haciendo el método de Euclides va a salir un número perfecto, pero ¿todos los números perfectos se forman mediante el método de Euclides? Esto es importante, porque si la respuesta es no, entonces no valdría este método.

Soleil

Hola :-)

En realidad, podría no haber creado las funciones take-left y take-right, ambas existen
en una librería llamada SRFI-1 (listas), que viene en la mayoría de intérpretes de Scheme.
(o está disponible aquí: http://srfi.schemers.org/srfi-1/srfi-1.html )

Crearlas ha servido para aprender cómo funcionan por dentro, que era el objetivo del rollo. : -P
La solución en si, son las últimas 5 lineas.

En Scheme puedes meter cualquier cosa dentro de una lista, incluyendo una macro, un vector
otra lista, funciones, ficheros, clases, continuaciones.... equal? te asegura que compara los
elementos internos de cada átomo en una lista al comparar aunque esos elementos sean
a su vez listas, vectores o hash tables.

Ejemplillo... list-overlap comparando dos listas
que contienen sublistas, funciones y vectores...
(y que contienen además al propio list-overlap!)

(define (square x) (* x x))
(list-overlap '(square 20 (20 square) list-overlap #(vector 9 9 1))
              '((20 square) list-overlap #(vector 9 9 1) 20)))

El programa muestra 3 por pantalla. : -)

Sobre tu primer algoritmo...

a = ["a", "b", "c", "p", "d", "d", "g"]
b = ["d", "d", "g", "h", "i"]

print solapamiento(a, b)

imprime 0, alguna cosilla hay mal por ahí : -P
El segundo funciona perfecto, es esencialmente lo mismo que recoger
elementos por izquierda o derecha, usando slices en Python. Como en Python
los slices son sintaxis propia del lenguaje queda muy limpio, es genial : -)

Sobre el método de los números perfectos...
El algoritmo de Euclides no es perfecto.
Se desconoce si hay por ejemplo números perfectos impares actualmente.
De todos modos, en la práctica es suficientemente bueno, puesto que usando el algoritmo
directo de todos los divisores sería tan lento que apenas conseguirías resultados.

EDIT: Sobre lo de C++...
Python o Scheme son lenguajes muy dinámicos, no sólo hay cosas que son mucho
más simples de hacer que en C++, sino que de hecho hay cosas que puedes hacer
en ellos que serían imposibles de hacer en C++. (por ejemplo, las continuaciones de
Scheme, o cosas que usen reflexión dinámica, las funciones anónimas...)

También hay que decir que C++ es más eficiente en velocidad de proceso y tiene acceso
a un bajo nivel que ni Python ni Scheme (ni Perl, Ruby etc...) tocan. El tipado fuerte de C++
también ayuda a ver errores en tiempo de compilación. En Python suelen suceder en tiempo
de ejecución. En Scheme lo mismo. (salvo en Typed Scheme o Oaklisp que son estáticos).

(PD: Tanta parrafada para decir que su objetivo es diferente xD)

Soleil

Ahí va un reto interesante : -)
http://projecteuler.net/index.php?section=problems&id=12

maRc

A ver si ahora cuando pase esta semana de exámenes me pongo a hacer algo de esto, que Python siempre me ha llamado la atención.

Puni

joe, no podiais haber escogido una fecha con mas examenes? aunq bueno a ver si me acuerdo de este thread dentro de un mes

B

#13: Al corregir el código me olvidé de quitarla, no hace falta al usar el extend (era para manejar el índice del array) xD. Lo edito luego, que ahora tengo prisa. A ver si saco algo de tiempo para la segunda prueba a lo largo de la semana.

maRc

Aquí está la mía para el primero. Tampoco me he calentado mucho la cabeza, que hace años que no toco python :P.

Números perfectos

Y aquí el segundo. Este ya está más bonito.

Solapamiento
B

Vamos, un poco de vida!! Yo pienso hacer la segunda 'prueba', pero es que no tendré tiempo hasta después del 30 de este mes :(

EDIT:

Prueba 2
MaKi

Ahora que estais empezando, es bueno que os acostumbreis a seguir las reglas de estilo que proponen los creadores de python, realmente tienen muy buenos consejos:

http://www.python.org/dev/peps/pep-0008/

maRc

Viendo que esto anda un poco parado. ¿Alguien sabe alguna web con ejercicios tipo los que ha puesto #1, ya sean para Python u otros lenguajes?

Usuarios habituales