Publicidad:
La Coctelera

Sugerencia de presentación

He nacido para vago

5 Agosto 2007

Siempre es posible ser más DRY

Que, cuando hablamos de programación (y yo lo hago bastante) no quiere decir que seas un seco, sino que no te repitas. Que, en este ámbito (en otros sería tema para otro post y probablemente otro autor ), es Algo Bueno (tm).

Quién no ha tenido que definir varios métodos que hacían casi lo mismo. El enfoque clásico sería la refactorización: un método que haga lo genérico, posiblemente aceptando algún parámetro para flexibilizarlo un poco, y que los métodos hagan sus particularidades, y deleguen en el primero lo que es genérico. Pero definir tooooodos esos métodos no deja de ser un coñazo (por más que sean cortos y simples). Jay Fields (si programas en Ruby y no lees su blog, ya lo estás añadiendo a tu lista; borra el mío aunque sea) nos enseña otra técnica, usando metaprogramación (programas que escriben programas, es tan sugerente, tan literario...).

El ejemplo (yo lo he simplificado porque yo soy más simple) está en Ruby pero supongo que será posible en muchos otros lenguajes. Al menos si molan tanto como Ruby

Primero vamos a crear el método que escriba nuestros métodos (ya que nosotros somos demasiado vagos para hacerlo nosotros mismos).

 class Class
  def def_each(*methods, &block)
   methods.each do |method|
    define_method method do
     yield(method)
    end
   end
  end
 end
 

Como somos así de chulos, lo definimos en la clase Class, así que estará disponible para cualquier clase que definamos. Si no lo entiendes, vuelve a leerlo, y si sigues sin entenderlo échale un ojo a la documentación de define_method y yield

Pues bien, definamos una clase, y usemos nuestro método-que-escribe-métodos:

 class Prueba
  def_each :uno, :dos, :tres do |method|
   method.to_s.size
  end
 end
 

Habéis leído bien: he definido tres métodos que devuelven el número de caracteres de su nombre:

 prueba = Prueba.new
 puts prueba.uno   [3]
 puts prueba.dos   [3]
 puts prueba.tres  [4]
 

Prometedme que lo usaréis para algo menos absurdo

Actualización: Si tuvísteis la curiosidad de probar el código, comprobaríais que funciona como se esperaba. Sin embargo, si me hicísteis caso y lo intentásteis emplear para algo más útil, os encontraríais con una pequeña dificultad, que hace que mi implementación de def_each sea, en realidad, inútil para cosas que no sean ejemplos triviales . El problema está en el uso de yield, que hace que el código se evalúe en el contexto de la clase y no de la instancia, de modo que no se puede acceder a atributos o métodos de la instancia. Vaya gracia. Pero como en esta vida todo tiene arreglo, y programando en Ruby más aún, he aquí la implementación correcta:

 class Class
  def def_each(*methods, &block)
   methods.each do |method|
    define_method method do
     instance_exec method, &block
    end
   end
  end
 end
 

Ojo, instance_exec es una novedad de Ruby 1.9, así que si usas una versión anterior (y si usas Rails es lo más probable), no funcionará. Sin problema tampoco, aquí (también cortesía de Jay) hay una implementación de instance_exec que funciona bien en 1.8.

Otra actualización: la mayoría podéis ignorar esta última advaertencia. Parece ser que Rails, en ese saco de sorpresas que es ActiveSupport, incluye su propia implementación de instance_exec.

servido por porras 5 comentarios compártelo

5 comentarios · Escribe aquí tu comentario

mamuso

mamuso dijo

Prometido! ya verás :)

5 Agosto 2007 | 08:49 PM

Pobrecito hablador

Pobrecito hablador dijo

Eso lleva en Lisp el huevo de tiempo :-) pero con otro nombre claro, son funciones de tercer nivel, funciones que reciben o devuelen otra funcion. Es lo bonito de Lisp que el codigo mismo es un literal.

Maravilloso lenguaje, que pena que se haya usado para hacer aberraciones como [X]Emacs

6 Agosto 2007 | 07:01 PM

porras

porras dijo

Sí, claro, ya dije que seguro que es posible en multitud de lenguajes, trataba de explicar la técnica y no la feature =:-P

De hecho hay que decir (y lo digo porque lo he leído, yo de Lisp más bien poco) que Ruby, o algunas partes de Ruby, se inspiran bastante en Lisp. Hay por ahí algún artículo de algún "Lisp hacker" comentándolo: http://www.randomhacks.net/articles/2005/12/03/why-ruby-is-an-acc...

16 Agosto 2007 | 10:38 PM

Juan Lupión

Juan Lupión dijo

Me parece una infamia que permitas en tu blogs blasfemias contra el Emacs. ¿Es que aqui no se pueden moderar comentarios?

22 Agosto 2007 | 09:25 AM

porras

porras dijo

Así me ahorran hacerlo a mí... =:-P

22 Agosto 2007 | 09:29 AM

Escribe tu comentario


Sobre mí

Avatar de porras

Sugerencia de presentación

ver perfil »
contacto »

Me llamo Sergio Gil Pérez de la Manga, y mi madre se cabrea si escribo mi nombre con un sólo apellido. Vivo, trabajo y hago casi todo lo demás en Madrid.

Trabajo como programador porque es lo más parecido que he encontrado a no trabajar. Sobre todo si lo haces bien. Y en eso estoy, en hacerlo cada vez mejor para trabajar cada vez menos. Alguno lo llamaría vagancia, y yo ahí no me meto.

Algunas de las herramientas que en este momento me llevan al Nirvana de no dar un palo al agua son Ruby, Ruby on Rails, Textmate, cualquier sabor de Unix (en este momento principalmente MacOSX pero también Ubuntu Linux) y sus herramientas, o Rake. En ocasiones hablo de ellas aquí, pienso que a alguien le pueden servir y que no puedo ser el único al que no le gusta trabajar.

Y como no sólo de tecnología vive el hombre (bueno, el hombre no sé, pero desde luego yo no), por aquí aparece de vez en cuando la punta del iceberg de mis pequeños pensamientos; al menos la parte de ellos que no cabe en Twitter.

Bienvenidos todos.

Y ahora: ¿Y tú?

Fotos

porras todavía no ha subido ninguna foto.

¡Anímale a hacerlo!

Buscar

suscríbete

Selecciona el agregador que utilices para suscribirte a este blog (también puedes obtener la URL de los feeds):

¿Qué es esto?

Crea tu blog gratis en La Coctelera