Serpientes y rubíes
Ja! Levanto el guante del desafío que plantea Gastón en su blog:
1. Dado un array con nombres de persona eliminar los nombre que comienzan con “Pe”:
juanjo@albus:~$ python
Python 2.5.1 (r251:54863, Mar 7 2008, 03:41:45)
[GCC 4.1.2 (Ubuntu 4.1.2-0ubuntu4)] on linux2
Type “help”, “copyright”, “credits” or “license” for more information.
>>> nombres = ['Pablo', 'Raul', 'Pedro', 'Pepe', 'Ariel', 'TerePe']
>>> [n for n in nombres if not n.startswith("Pe")]
['Pablo', 'Raul', 'Ariel', 'TerePe']
2. Verificar si el mismo array contiene el nombre “Raul”:
>>> "Raul" in nombres
True
3. Generar un string con todos los nombres unidos por “-”:
>>> "-".join(nombres)
'Pablo-Raul-Pedro-Pepe-Ariel-TerePe'
4. Generar un segundo array con los nombres todos en minúsculas ordenado alfabéticamente:
>> sorted([n.lower() for n in nombres])
['ariel', 'pablo', 'pedro', 'pepe', 'raul', 'terepe']
5. Desordenar el array:
>>> from random import shuffle
>>> shuffle(nombres)
>>> nombres
['Pepe', 'Pedro', 'Ariel', 'Raul', 'Pablo', 'TerePe']
6. Averiguar si la lista siguiente tiene números pares:
>>> bool([n for n in numeros if n % 2 == 0])
True
7. Averiguar si toda la lista son números pares:
>>> len(numeros) == len([n for n in numeros if n % 2 == 0])
False
8. Obtener el producto de una lista de números:
>>> f = lambda x,y: x*y
>>> reduce(f, numeros)
120
9. Obtener el factorial de 9999:
>>> reduce(f, xrange(1, 10000))
# la respuesta tiene 35656 caracteres.
10. Averiguar si dos arrays son iguales:
>>> [1,2,3,4] == [1,2,3,4]
True
¿Conclusiones? Creo que las listas por comprensión de la serpiente le gana a los .metodos del rubí. Pero en 6 y 7 perdemos feo :-/ ¿Algún pythonista que reescriba esos ejercicios?
0 votos


Abril 20th, 2008 at 3:00 am
6)
any([x % 2 == 0 for x in [1,2,3,4,5]])7)
all([x % 2 == 0 for x in [1,2,3,4,5]])Abril 20th, 2008 at 10:04 am
Muy buenas esas dos! No las conocía.
Ayer justo escribí algo como:
if False not in lista:
Quedaría mejor:
if not all(lista):
?
Abril 20th, 2008 at 12:28 pm
Hola Juanjo! no era para levantar el guante! ni para pelearnos como aclaro en mi blog, aclarado eso.
1. Me parece que como conclusión saco que ruby tiene una sintaxis mas elegante (Syntactic sugar), fijate por ejemplo el punto 1
[n for n in nombres if not n.startswith("Pe")] no se entiende mucho que hace “n for n in nombres”, en cambio
“[].delete_if{|n| n =~ /^Pe/}” es mucho más expresivo “borrar si”, a mí me gusta más pero es una cuestión de gustos.
2. Los dos lenguajes se parecen mucho
Abril 20th, 2008 at 7:11 pm
En los puntos 8 y 9 no podés usar el lambda directamente?
Serían
reduce(lambda x,y: x*y, [1,2,3,4,5])y
reduce(lambda x,y: x*y, xrange(1, 10000))respectivamente.
Por otro lado, para mi a python le falta poder escribir funciones anónimas de más de una línea (o sea lambdas con varios statements) para ser potente. Si te fijás, un montón de las soluciones de python son con sintaxis específica, mientras que en ruby no hay sintaxis ad hoc (como por ejemplo, los list-comprehensions y a in b). Igualmente, los 2 lenguajes son hermosos comparados con java o php.
Abril 21st, 2008 at 10:04 am
Gastón:
No sé si elegante, pero si “[].delete_if{|n| n =~ /^Pe/}” es la mejor forma de escribir en Ruby, confirmo que *me* gusta mucho más la sintaxis de Python que eso…
Aureliano:
No entiendo porqué una función anónima multilinea es más poderosa que una función nominada multilinea.
Slds.
Abril 21st, 2008 at 6:01 pm
Facundo:
Otras variantes serían:
["Pedro", "Pepe", "Ariel", "TerePe"].select{|n| not n.start_with? “Pe”}
["Pedro", "Pepe", "Ariel", "TerePe"].delete_if{|n| n.start_with? “Pe”}
["Pedro", "Pepe", "Ariel", "TerePe"].find_all{|n| not n.start_with? “Pe”}
["Pedro", "Pepe", "Ariel", "TerePe"].collect{|n| n if not n.start_with? “Pe”}.compact
a mí delete_if me gusta más por que me parece que es más expresivo, vos por qué decís que te gusta más el “n for n in nombres ” ?
Saludos
Abril 22nd, 2008 at 11:05 am
Yo la 8, por claridad, lo haría como:
import operator
reduce (operator.mul, numeros)
Abril 24th, 2008 at 10:00 pm
Facundo:
Eso se puede hacer por que la regex que utilizó Gastón es sencilla, pero si es algo un poquito más complicado él puede seguir haciendo =~ y con Python tenemos que importar un módulo (re), ejecutar la expresión sobre la cadena y ver si matchea para incluirla en la lista o no. Con una regex más complicada no tenemos una agradable sintaxis visualmente.
Saludos.
Abril 25th, 2008 at 9:20 am
El Zen de Python habla sobre que la "legibilidad del código cuenta". El uso - y abuso - de expresiones regulares no ayuda para nada a la legibilidad ni a la depuración.
Si para validar un número telefónico te pongo /^((\(\d{3}\))?*\d{3}(-|)\d{4},?*)+$/ , cuando revises el código dentro de un año pegate un tiro si no has comentado en varias líneas lo que todo eso significa
Abril 25th, 2008 at 12:00 pm
Gastón:
1. nombres.delete_if{|n| n =~ /^Pe/}
2. [x for x in nombres if not x.startswith("Pe")]
Te cuento por qué no me gusta la versión 1, teniendo en cuenta que *no sé Ruby*:
- ¿Eso me devuelve algo nuevo o me está modificando “nombres”?
- Asumo, que el “|n|” es para nombrar cada elemento de “nombres”, y que el “=~” hace una especie de “comparación soft”, ¿pero para qué son las barras alrededor de “^Pe?
- Si “nombres” es un objeto, y “delete_if” es un método del objeto, ¿eso significa que a un método lo ejecutás poniendo {}?
Muy posiblemente, si no sabés Python, te perderás algunos detalles del punto 2, pero creo que en general es más fácil de leer y de descubrir lo que está haciendo (si no sos japonés, al menos)
Igual, para cerrar, creo que esto que decimos ambos es un 99% cuestión de gustos…
Humitos:
Las expresiones regulares están sobrevaloradas. En el 99% de mis códigos, jamás necesito una (principalmente porque Python tiene otras formas de manejar strings…). Por otro lado, si querés claridad, en Python podés usar el modo verbose, con lo cual explicás la regex adentro de la misma, mirá: http://docs.python.org/dev/library/re.html#re.VERBOSE.
Slds.
Abril 25th, 2008 at 8:03 pm
Hola Facundo, las { } no son para ejecutar un método, son para delimitar un bloque de código, en ruby los métodos pueden tomar bloques de código multilínea cómo argumento,
otra forma sería:
nombres.delete_if do |n|
n.start_with? “Pe”
end
Si no te gustan las llaves podés usar do y end como delimitadores, Fijate este ejemplo:
nombres.delete_if do |n|
puts “El nombre #{n} empieza con Pe” if n.start_with? “Pe”
puts “#{n} no empieza con Pe” unless n.start_with? “Pe”
end
Otra forma con for sería:
for n in nombres
puts “El nombre #{n} empieza con Pe” if n.start_with? “Pe”
puts “#{n} no empieza con Pe” unless n.start_with? “Pe”
end
con respecto a tu pregunta, delete_if retorna un nuevo array, no modifica a nombres
Entonces el tema es así delete_if es un método que toma como argumento un bloque de código si este bloque evalúa a true entoces n se elimina.
César:
Es verdad que las regex complicadas son difíciles de leer pero eso no quiere decir que no se deban usar, hay veces que son muy útiles, por ejemplo para las validaciones de entrada de datos y por lo que decís de la depuración, para eso están los tests.
Saludos