Bienvenidos a un lugar donde la programación, los vídeos, las divagaciones y las subnormalidad se combinan para demostrar que el caos puede ser bello.
Serabe Reloaded
Conecto ergo sum. Non conecto ergo urgueo.
"Yo no sé si Dios existe, pero si existe, sé que no le va a molestar mi duda."
Mario Benedetti
Problema 1
Empiezo aquí una serie de artículos de periodicidad variable que lo único que pretende es ser algo similar (de una manera muy amplia) a PyEuler. La lista completa de problemas está en el Proyecto Euler y, como no podía ser de otra manera, empiezo por el primero.
Enunciado:
Halla la suma de todos los números menores que 1000 y múltiplos de 3 o de 5.
Solución 1:
-
def euler1a(e, numbers)
-
(1..e).select{|x| numbers.any?{|y| (x%y == 0)}}.inject{|memo,o| memo+=o}
-
end
-
-
puts euler1a(1000,[3,5])
La explicación es simple. Tenemos un rango desde 1 hasta el límite, filtramos los que son múltiplos de algún elemento del array y después se suman.
Solución 2:
-
def euler1b(e,numbers)
-
(1..e).inject(0){|memo,o| (numbers.any?{|x| (o%x)==0}) ? memo+o : memo }
-
end
-
-
puts euler1b(1000,[3,5])
Éste es similar al anterior, sólo que se suma según se recorre el rango.
Solución 3:
Para este necesitamos antes un par de funciones. La primera, halla el máximo común divisor de dos números. Para ello, básicamente usa el algoritmo de Euclides.
-
def mcd(n1,n2)
-
if n1 <n2
-
mcd(n2,n1)
-
elsif (n1%n2) == 0
-
n2
-
else
-
mcd(n2,n1%n2)
-
end
-
end
Por otra parte, está la función que calcula el mínimo común múltiplo. Para ello usa su relación con el m.c.d..
-
def mcm(n1,n2)
-
n1*n2/mcd(n1,n2)
-
end
Con esto, ya podemos pasar a la tercera solución, que es diferente de las otras dos en que sólo sirve si utilizamos dos números como filtro.
-
def euler1c(limit, n1, n2)
-
(n1*((limit/n1)*((limit/n1)+1)/2)+n2*((limit/n2)*((limit/n2)+1)/2)-mcm(n1,n2)*((limit/mcm(n1,n2))*((limit/mcm(n1,n2))+1)/2))
-
end
-
-
puts euler1c(1000,3,5)
La explicación es un poco más complicada, pues implica teoría básica de conjuntos y la conocida fórmula de la suma de 1 a n.
Ahora es vuestro turno. ¿Cómo se os ocurre hacerlo?
Resolución: ejercicio y números confuso.
Hace tiempo que conozco el blog Programancia101, un blog con "retos" de programación. Hace tiempo sacaron éste, como algún otro lo leí, pero pasó que estaba en una época de mi vida en la que me apetecía mucho programar, y me decidí a resolverlo. El problema, básicamente, consiste en, dadas una reglas de "confusión" y un número, averiguar las posibles confusiones, tanto al derecho, como al revés. Sé que parece lioso, por eso os remito al enunciado , otra vez. Sería interesante que fueseis viendo el código fuente al mismo tiempo y al menos haberlo pensado un par de veces (si no, no hay diversión).
Nota previa.
Aunque hable de número, internamente es más cómodo, en este caso, trabajar con cadenas. Por lo tanto, aunque diga número, el tipo es String.
Paso 1: Almacenamiento de las "confusiones".
Para almacenar las confusiones opté por un hash que contenía otros dos: straight (para las confusiones al derecho) y reverse (para las confusiones si están dados la vuelta). En cada uno de ellos, a cada número se le asigna un array con las posibles confusiones (dadas en el enunciado). Es obvio, que en el hash straight deberán estar también contenidos los propios números. El hash se define desde la línea 22 a la 48.
Paso 2: Redefiniendo la clase String para nuestros oscuros propósitos.
En la clase String, se necesitan dos nuevos métodos: little y reversible?. El primero devuelve toda la cadena excepto el primer carácter, para qué, en el siguiente paso. El método reversible? comprueba si todos los números son reversibles, es decir, el array de confusiones del hash CONFUSIONS[:reverse][numero.to_sym] es no vacío. Esto se hace de la línea 4 a la 19.
Paso 3: La idea.
La idea de la resolución es muy sencilla. Dado un número, las posibles confusiones se corresponden con las confusiones del mismo número sin la unidad de mayor orden (por ejemplo, si el número es el 12345, el número sin la unidad de mayor orden es el 2345) combinadas con las posibles confusiones del primer número (en el caso anterior, el 1). Por lo tanto, aquí entra la recursión, y de ahí la necesidad del método little de la clase String (recordemos que los números, en este caso, los guardamos como cadenas). El método en cuestión recibe dos parámetros, el número y el hash de confusiones, que por defecto es CONFUSIONS[:straight]. Por supuesto, necesitamos de otra función. En ella, lo que hacemos es obtener el array de confusiones al derecho y mezclarlo con el de las confusiones al revés, si las hubiera. Una vez hecho esto, se eliminan duplicados y se ordena. Estas funciones se encuentran definidas desde la línea 53 a la 75.
Paso 4: Los retoques.
Por último, añadir una comprobación de los parámetros (se ha de recibir un único parámetro que sea un número). Esto se realiza de la línea 78 a la 82. Posteriormente se llama a la función (línea 83) y se muestran los resultados (línea 85).
Anidamiento de recursos con REST en Ruby on Rails ó cómo acceder de dos maneras distintas a un mismo controlador utilizando REST con Ruby On Rails.
Después de un título tan largo, lo primero es meter en situación. Tengo una web que tiene temas, en cada tema hay más temas y descargas. Estos se muestran con los controladores topics y downloads respectivamente. El problema venía puesto que los controladores se tenían que poder acceder de dos formas. En el caso de las descargas:
1.- /downloads
2.- /topics/:topic_id/downloads
En el caso de los temas:
1.- /topics
2.- /topics/:topic_id/topics
Aproximación: Crear un segundo controlador.
Empecemos por el tema de los temas y sus subtemas. En este caso utilizaremos un segundo controlador al que llamaremos subtopic. Para poder acceder de la forma buscada hay que sustituir en el archivo config/routes.rb la línea
map.resources :topics
por lo siguiente:
map.resources :topics do |topic|
topic.resources :subtopics, :path_prefix => "topics/:topic_id"
end
Después, se eliminan los métodos delete, edit, show y update, pues sólo se necesitan los métodos create, index y new. Además, lo más lógico sería implementar los métodos create, index y new de forma que podamos aprovechar las vistas del controlador topic. Aún así, tenemos repetición, lo que viola el principio DRY que rige a Rails. Por lo tanto, veamos cómo podemos conseguir el resultado obtenido de una forma más sencilla y elegante.
Fase final: las cosas bien hechas.
En este caso cogeremos como ejemplo las descargas de un tema. Para ello, al igual que en el ejemplo anterior, hay que modificar el routes.rb añadiéndole las siguientes líneas
map.resources :topics do |topic|
topic.resources :downloads, :path_prefix => "topics/:topic_id", :name_prefix => 'topic_'
end
map.resources :downloads
Hay que recalcar dos aspectos de estas rutas. En primer lugar, el :name_prefix. De esta forma, podremos diferenciar entre si hay que crear un path del tipo /downloads/1 o del tipo /topics/:topic_id/downloads simplemente precediendo el nombre de los métodos que construyen el path con topic_. Es decir, si utilizamos el método edit_download_path(download) obtenemos la ruta http://localhost:3000/downloads/2;edit. En cambio, al utilizar el método topic_edit_download_path(download.topic,download) (hay que recalcar que el :name_prefix va antes del nombre normal del método y que los parámetros se pasan según el orden en el que se encuentran en la ruta) obtenemos la ruta http://localhost:3000/topics/1/downloads/2;edit.
Espero que este artículo os haya sido tan interesante (aunque me exprese como el culo) como para mí fue el investigar cómo se hace esta monería con REST, Ruby y Ruby on Rails.
Comparativa I: Cliente en Ruby
Pasamos de Java a Ruby, un lenguaje que a pesar de ser aproximadamente tan viejo como el primero, aún sigue siendo desconocido por algunos europeos. Hace poco ha tenido un auge en el viejo continente gracias, en gran medida, a RubyOnRails.
Puesto que vamos a necesitar realizar conexiones, lo primero es:
require 'socket'
Acto seguido, se define el método mensaje_aleatorio:
def mensaje_aleatorio
msg = ["Hola, Mundo","Melón.","Larga y próspera vida.","La Quinta Raza.","So long, and thanks for all the fish.","Spain is different.", "Com va això?","Bona tarda."]
msg[rand(msg.length)]
end
Y ahora llega la magia Ruby. Para crear diez hilos diferentes simplemente se utiliza el método times y la clase Thread:
num_hilos = 10
num_hilos.times do |i|
Thread.start do
En él, se crea un bucle infinito en el que se conecta al servidor (simplemente con TCPSocket.new(servidor,puerto) ), envía el mensaje y espera la respuesta, comprobando que es el texto enviado.
while(true)
puts "Abriendo socket #{j}"
begin
socket = TCPSocket.new("localhost",7777)
puts "Abierto socket #{j}"
s = mensaje_aleatorio
socket.puts s
puts "Enviado \"#{s}\" en #{j}"
puts (s == (s1=socket.gets().strip())) ? "Correcto" : "Falló\nEsperado: #{s}\nDevuelto: #{s1}"
rescue Exception => bang
puts bang.to_s
ensure
socket.close unless socket.nil?
end
end
Ya, por último y fuera del método times, se crea un bucle infinito para evitar que termine el script sin hacer lo que se le pide.
Aquí el código completo.
"La Física es como el sexo. Está claro que puede tener algunos resultados prácticos. Pero no lo hacemos por eso."
Richard P. Feynman, físico.