Manual de Ruby

download Manual de Ruby

of 64

description

Es un manual para entender Ruby

Transcript of Manual de Ruby

  • Gua del usuario de Ruby

    matz

  • Gua del usuario de Rubypor matz

  • Tabla de contenidosContenidos ......................................................................................................................................................................i1. Qu es Ruby?...........................................................................................................................................................12. Inicio...........................................................................................................................................................................23. Ejemplos sencillos .....................................................................................................................................................4

    3.1. El bucle de entrada/evaluacin .......................................................................................................................54. Cadenas......................................................................................................................................................................65. Expresiones regulares ...............................................................................................................................................96. Arrays.......................................................................................................................................................................127. Hashes ......................................................................................................................................................................138. Retomando los ejemplos sencillos..........................................................................................................................14

    8.1. Factoriales ....................................................................................................................................................148.2. Cadenas ........................................................................................................................................................158.3. Expresiones regulares...................................................................................................................................16

    9. Estructuras de control ............................................................................................................................................189.1. case ...............................................................................................................................................................189.2. while .............................................................................................................................................................189.3. for .................................................................................................................................................................19

    10. Iteradores...............................................................................................................................................................2111. Pensamiento orientado a objetos .........................................................................................................................2412. Mtodos..................................................................................................................................................................2613. Clases......................................................................................................................................................................2814. Herencia .................................................................................................................................................................3015. Redefinicin de mtodos .......................................................................................................................................3216. Control de accesos.................................................................................................................................................3417. Mtodos singleton..................................................................................................................................................3618. Mdulos..................................................................................................................................................................3719. Objetos procedimiento..........................................................................................................................................3920. Variables ................................................................................................................................................................41

    20.1. Variables globales.......................................................................................................................................4120.2. Variables de instancia .................................................................................................................................4220.3. Variables locales .........................................................................................................................................43

    21. Constantes..............................................................................................................................................................4622. Procesamiento de excepciones: rescue ................................................................................................................4823. Procesamiento de excepciones: ensure................................................................................................................5024. Accesores................................................................................................................................................................51

    24.1. El mtodo inspect ......................................................................................................................................5124.2. Facilitando la creacin de accesores ..........................................................................................................5224.3. Ms diversin con la fruta ..........................................................................................................................53

    iii

  • 25. Inicializacin de objetos .......................................................................................................................................5425.1. El mtodo initialize....................................................................................................................................5425.2. Modificando suposiciones por requisitos ...................................................................................................5425.3. Inicializacin flexible .................................................................................................................................54

    26. Entresijos ...............................................................................................................................................................5626.1. Delimitadores de sentencias .......................................................................................................................5626.2. Comentarios ...............................................................................................................................................5626.3. Organizacin del cdigo.............................................................................................................................5626.4. Esto es todo ................................................................................................................................................58

    iv

  • Lista de tablas5-1. Caracteres especiales en expresiones regulares.......................................................................................................920-1. Clases de variables...............................................................................................................................................4120-2. Variables de sistema.............................................................................................................................................4224-1. Accesores.............................................................................................................................................................52

    v

  • ContenidosRuby es un lenguaje de programacin orientado a objetos sencillo. Al principio puede parecer un poco extrao, perose ha diseado para que sea fcil de leer y escribir. Esta Gua del usuario de Ruby permite ejecutar y utilizar Ruby yproporciona una visin de la naturaleza de Ruby que no se puede obtener del manual de referencia.

    i

  • Captulo 1. Qu es Ruby?Ruby es un lenguaje de guiones (scripts) para una programacin orientada a objetos rpida y sencilla. Qu significaesto?

    Lenguaje de guiones interpretado:

    Posibilidad de realizar directamente llamadas al sistema operativo

    Potentes operaciones sobre cadenas de caracteres y expresiones regulares

    Retroalimentacin inmediata durante el proceso de desarrollo

    Rpido y sencillo:

    Son innecesarias las declaraciones de variables

    Las variables no tienen tipo

    La sintaxis es simple y consistente

    La gestin de la memoria es automtica

    Programacin orientada a objetos:

    Todo es un objeto Clases, herencia, mtodos, ...

    Mtodos singleton

    Mixins por mdulos

    Iteradores y cierres

    Tambin:

    Enteros de precisin mltiple

    Modelo de procesamiento de excepciones

    Carga dinmica

    Hilos

    Si no ests familiarizado con alguno de los trminos anteriores, contina leyendo y no te preocupes. El mantra deRuby es Rpido y Sencillo .

    1

  • Captulo 2. InicioInicialmente hay que comprobar si se tiene instalado Ruby. Desde la lnea de peticin de comandos de la shell (aqula representaremos por %, por lo tanto no introducir el % de los ejemplos), tecleamos:

    % ruby -v

    (-v le indica al intrprete que imprima la versin de Ruby), a continuacin pulsamos la tecla Enter. Si est instaladoRuby, aparecer el siguiente mensaje o algo similar:

    % ruby -vruby 1.6.3 (2001-11-23) [i586-linux]

    Si no est instalado, pide a tu administrador que lo instale, o hazlo t mismo dado que Ruby es software libre sinrestricciones de instalacin o uso.

    Comencemos ahora a jugar con Ruby. Se puede introducir directamente en la lnea de comandos un programa Rubyutilizando la opcin -e:

    % ruby -e print "hola mundo\n"hola mundo

    Un programa Ruby se puede almacenar en un fichero, lo que es mucho ms adecuado.

    % cat > test.rbprint "hola mundo\n"^D% cat test.rbprint "hola mundo\n"%ruby test.rbhola mundo

    ^D es control-D. Lo anterior es vlido para UNIX. Si se est utilizando DOS, prueba con:

    C:\ruby> copy con: test.rbprint "hola mundo\n"^ZC:\ruby> type test.rbprint "hola mundo\n"c:\ruby> ruby test.rbhola mundo

    Al escribir cdigo con ms fundamento que ste, se puede utilizar cualquier editor!.

    Algunas cosas sorprendentemente complejas y tiles se pueden hacer con programas miniatura que caben en la lneade comandos. Por ejemplo, el siguiente programa reemplaza la cadena foo por bar en todos los ficheros cabecera yfuentes C del directorio de trabajo, realizando una copia de seguridad del fichero original a la que aade .bak

    % ruby -i .bak -pe sub "foo", "bar" *.[ch]

    2

  • Captulo 2. Inicio

    El siguiente programa funciona como el comando cat de UNIX (aunque es ms lento):

    % ruby -pe 0 file

    3

  • Captulo 3. Ejemplos sencillosEscribamos una funcin que calcula factoriales. La definicin matemtica de factorial es la siguiente:

    (n==0) n! = 1(sino) n! = n * (n-1)!

    En Ruby se puede escribir as:

    def fact(n)if n == 0

    1else

    n * fact(n-1)end

    end

    Se puede apreciar la aparicin repetida de end. Debido a esto a Ruby se le conoce como un lenguaje tipo Algol.(Realmente la sintaxis de Ruby reproduce con ms exactitud la del lenguaje Eiffel). Tambin se puede apreciar lafalta de la sentencia return. Es innecesaria debido a que una funcin Ruby devuelve lo ltimo que haya evaluado. Lautilizacin de return es factible aunque innecesaria.

    Probemos la funcin factorial. Aadiendo una lnea de cdigo obtenemos un programa funcional:

    # Programa para hallar el factorial de un nmero# Guarda este programa como fact.rb

    def fact(n)if n == 0

    1else

    n * fact(n-1)end

    end

    print fact(ARGV[0].to_i), "\n"

    Aqu, ARGV es un array que contiene los parmetros de la lnea de comandos y to_i convierte una cadena de carac-teres a un entero.

    % ruby fact.rb 11% ruby fact.rb 5120

    Funcionara con un parmetro igual a 40? Este valor podra provocar un desbordamiento en una calculadora...

    % ruby fact.rb 40815915283247897734345611269596115894272000000000

    4

  • Captulo 3. Ejemplos sencillos

    Funciona. Adems Ruby puede tratar cualquier entero que quepa en la memoria del ordenador. Por lo que se puedecalcular el factorial de 400!:

    % ruby fact.rb 40064034522846623895262347970319503005850702583026002959458684445942802397169186831436278478647463264676294350575035856810848298162883517435228961988646802997937341654150838162426461942352307046244325015114448670890662773914918117331955996440709549671345290477020322434911210797593280795101545372667251627877890009349763765710326350331533965349868386831339352024373788157786791506311858702618270169819740062983025308591298346162272304558339520759611505302236086810433297255194852674432232438669948422404232599805551610635942376961399231917134063858996537970147827206606320217379472010321356624613809077942304597360699567595836096158715129913822286578579549361617654480453222007825818400848436415591229454275384803558374518022675900061399560145595206127211192918105032491008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

    No podemos verificar su validez a simple vista, pero lo debe ser ;-).

    3.1. El bucle de entrada/evaluacinAl ejecutar Ruby sin parmetros, ste lee de la entrada estndar comandos y los ejecuta despus de dar por finalizadala entrada:

    % rubyprint "hola mundo\n"print "adis mundo\n"^Dhola mundoadis mundo

    Ruby incluye un programa llamado eval.rb que permite la introduccin de cdigo desde el teclado a travs de unbucle iterativo que muestra los resultados a medida que se obtienen. Se utilizar ampliamente a lo largo del tutorial.

    Si se tiene un terminal ANSI (esto ser casi seguro si se est ejecutando alguna versin de UNIX; bajo DOS se debeinstalar ANSI.SYS o ANSI.COM) se debera utilizar este eval.rb mejorado que permite autoindentacin, informessobre incidencias y resaltado por color. Si no, en el directorio sample de la distribucin Ruby existe una versin noANSI que funciona con cualquier terminal. A continuacin se muestra una pequea sesin con eval.rb:

    %ruby eval.rbruby> print "hola mundo\n"hola mundo.

    nilruby> exit

    print produce hola mundo. La siguiente lnea, es este caso nil informa sobre lo ltimo que se ha evaluado; Ruby nodistingue entre sentencias y expresiones, por lo tanto la evaluacin de una pieza de cdigo significa bsicamente lomismo que ejecutarla. Aqu nil, indica que print no devuelve ningn valor significativo. Obsrvese que se puede salirdel bucle de interpretacin con exit, aunque tambin funciona ^D.

    A lo largo de toda esta introduccin ruby> representa la peticin de entrada del pequeo pero til programa eval.rb.

    5

  • Captulo 4. CadenasRuby maneja tanto cadenas como datos numricos. Las cadenas pueden estar entre comillas dobles("...") o comillassimples (...).

    ruby> "abc""abc"ruby> abc"abc"

    Las comillas simples y dobles a veces tienen efectos diferentes. Una cadena de comillas dobles permite la presenciaembebida de caracteres de escape precedidos por un backslash y la expresin de evaluacin #{ }. Una cadena decomillas simples no realiza esta evaluacin, lo que se ve es lo que se obtiene. Ejemplos:

    ruby> print "a\nb\nc","\n"a

    bc

    nilruby> print a\nb\nc,"\n"a\nb\ncnilruby> "\n""\n"ruby> \n"\\n"ruby> "\001""\001"ruby> \001"\\001"ruby> "abcd #{5*3} efg""abcd 15 efg"ruby> var = " abc "" abc "ruby> "1234#{var}5678""1234 abc 5678"

    El manejo de las cadenas en Ruby es ms inteligente e intuitivo que en C. Por ejemplo, se pueden concatenar cadenascon + y se puede repetir una cadena varias veces con *:

    ruby> "foo" + "bar""foobar"ruby> "foo" * 2"foofoo"

    La concatenacin de cadenas en C es ms incmoda debido a la necesidad de una gestin explcita de la memoria:

    char *s = malloc(strlen(s1)+strlen(s2) +1);strcpy(s, s1);

    6

  • Captulo 4. Cadenas

    strcat(s, s2);/* ... */free(s);

    Ya que al usar Ruby no tenemos que considerar el espacio que va a ocupar una cadena, estamos liberados de la gestinde la memoria.

    A continuacin se muestran algunas cosas que se pueden hacer con las cadenas.

    Concatenacin:

    ruby> word = "fo" + "o""foo"

    Repeticin:

    ruby> word = word * 2"foofoo"

    Extraccin de caracteres (obsrvese que los caracteres en Ruby son enteros):

    ruby> word[0]102 # 102 es el cdigo ASCII de fruby> word[-1]111 # 111 es el cdigo ASCII de o

    (Los ndices negativos indican desplazamientos desde el final de la cadena, en vez del comienzo).Extraccin de subcadenas:

    ruby> herb = "parsley""parsley"ruby> herb[0,1]"p"ruby> herb[-2,2]"ey"ruby> herb[0..3]"pars"ruby> herb[-5..-2]"rsle"

    Verificacin de la igualdad:

    ruby> "foo" == "foo"trueruby> "foo" == "bar"false

    Pongamos ahora alguna de estas caractersticas en uso. Este acertijo es "Adivina la palabra", aunque tal vez la palabra"acertijo" es demasiado elevada para lo que viene ;-).

    7

  • Captulo 4. Cadenas

    # Salvar como guess.rbwords = [foobar, baz, quux]secret = words[rand(3)]

    print "adivina? "while guess = STDIN.gets

    guess.chop!if guess == secret

    print "Ganas!\n"break

    elseprint "Lo siento. Pierdes\n"

    endprint "adivina? "

    endprint "La palabra era ", secret, ".\n"

    Por el momento no te preocupes de los detalles del cdigo. A continuacin se muestra una ejecucin del programa delacertijo.

    $ ruby guess.rbadivina? foobarLo siento. Pierdes.adivina? quuxLo siento. Pierdes.adivina? ^DLa palabra era baz.

    (Debera haberlo hecho mejor dada la probabilidad de 1/3 de acertar).

    8

  • Captulo 5. Expresiones regularesRealicemos un programa mucho ms interesante. Es el momento de comprobar si una cadena satisface una descrip-cin, que llamaremos patrn.

    En estos patrones existen algunos caracteres y combinaciones de caracteres que tienen un significado especial, y son:

    Tabla 5-1. Caracteres especiales en expresiones regulares

    Smbolo Descripcin>[] Especificacin de rango. (p.e. [a-z] representa una letra en el rango de la a a la z\w Letra o dgito; es lo mismo que [0-9A-Za-z]\W Ni letra, ni dgito\s Espacio, es lo mismo que [ \t\n\r\f]\S No espacio\d Dgito; es lo mismo que [0-9]\D No dgito\b Backspace (0x08) (slo si aparece en una especificacin de rango)\b Lmite de palabra (slo si no aparece en una especificacin de rango)\B No lmite de palabra* Cero o ms repeticiones de lo que precede+ Una o ms repeticiones de lo que precede[m,n] Al menos m y como mximo n de lo que precede? Al menos una repeticin de lo que precede; es lo mismo que [0,1]| Puede coincidir con lo que precede o con lo que sigue() Agrupamiento

    El trmino comn para esto patrones que utilizan este extrao vocabulario es expresin regular. En Ruby, como enPerl, normalmente estn rodeadas por barras inclinadas en vez de por comillas dobles. Si nunca antes se ha trabajadocon expresiones regulares, es probable que parezcan cualquier cosa excepto regulares, pero sera inteligente dedicaralgn tiempo a familiarizarse con ellas. Tienen un poder expresivo en su concisin que puede evitar muchos do-lores de cabeza (y muchas lneas de cdigo) si se necesita realizar coincidencia de patrones o cualquier otro tipo demanipulacin con cadenas de texto.

    Por ejemplo, supongamos que queremos comprobar si una cadena se ajusta a esta descripcin: Comienza con unaf minscula, a la que sigue exactamente una letra mayscula y opcionalmente cualquier cosa detrs de sta, siemprey cuando no haya ms letras minsculas. Si se es un experimentado programador en C probablemente se hayaescrito este tipo de cdigo docenas de veces, verdad? Admitdmoslo, es difcil mejorarlo. Pero en Ruby slamentees necesario solicitar que se verifique la cadena contra la siguiente expresin regular /^f[A-Z][^a-z]*$/.Y que decir de Contiene la cadena un nmero hexadecimal entre ngulos? No hay problema.

    ruby> def chab(s) # contiene la cadena un hexadecinal entre ngulosruby| (s =~ //) != nilruby| endnil

    9

  • Captulo 5. Expresiones regulares

    ruby> chab "Este no es"falseruby> chab "Puede ser esta? (0x35)" # entre parntesis, no ngulosfalseruby> chab "O esta? " # letra errneafalseruby> chab "OK esta si; "true

    Aunque inicialmente las expresiones regulares pueden parecer enigmticas, se gana rpidamente satisfaccin al sercapaz de expresarse con tanta economa.

    A continuacin se presenta un pequeo programa que nos permitir experimentar con las expresiones regulares.Almacenmoslo como regx.rb, se ejecuta introduciendo en la lnea de comandos ruby regx.rb

    # necesita un terminal ANSI!!!

    st = "\033[7m"en = "\033[m"

    while TRUEprint "str> "STDOUT.flushstr = getsbreak if not strstr.chop!print "pat> "STDOUT.flushre = getsbreak if not rere.chop!str.gsub! re, "#{st}\\{en}"print str, "\n"

    endprint "\n"

    El programa necesita dos entradas, una con la cadena y otra con la expresin regular. La cadena se comprueba contrala expresin regular, a continuacin muestra todas las partes de la cadena que coindicen con el patrn en vdeo inverso.No nos preocupemos de los detalles; analizaremos el cdigo posteriormente.

    str> foobarpat> ^fo+foobar~~~

    Lo resaltado es lo que aparecer en vdeo inverso como resultado de la ejecucin del programa. La cadena ~~~ esen beneficio de aquellos que usen visualizadores en modo texto.

    Probemos algunos ejemplos ms.

    str> abc012dbcd555pat> \dabc012dbcd555

    10

  • Captulo 5. Expresiones regulares

    ~~~ ~~~

    Sorprendentemente y como indica la tabla al principio de este captulo: \d no tiene nada que ver el carcter d, sinoque realiza la coincidencia con un dgito.

    Qu pasa si hay ms de una forma de realizar la coincidencia con el patrn?.

    str> foozboozerpat> f.*zfoozboozer~~~~~~~~

    se obtiene foozbooz en vez de fooz porque las expresiones regulares tratan de obtener la coincidencia ms larga posible.A continuacin se muestra un patrn para aislar la hora de un campo limitada por dos puntos.

    str> WedFeb 7 08:58:04 JST 2001pat> [0-9]+:[0-9]+(:[0-9]+)?WedFeb 7 08:58:04 JST 2001

    ~~~~~~~~

    =~ es el operador de coincidencia con expresiones regulares; devuelve la posicin en la cadena donde se ha producidouna coincidencia o nil si no la hay.

    ruby> "abcdef" =~ /d/3ruby> "aaaaaa" =~ /d/nil

    11

  • Captulo 6. ArraysSe pueden crear un array listando elementos entre corchetes ([ ]) y separndolos por comas. Los arrays en Rubypueden almacenar objetos de diferentes tipos.

    ruby> ary = [1, 2, "3"][1, 2, "3"]

    Los arrays se pueden concatenar y repetir, igual que las cadenas.

    ruby> ary + ["foo", "bar"][1, 2, "3", "foo", "bar"]ruby> ary * 2[1, 2, "3", 1, 2, "3"]

    Se pueden utilizar ndices numricos para acceder a cualquier parte del array.

    ruby> ary[0]1ruby> ary[0,2][1, 2]ruby> ary[-2]2ruby> ary[-2,2][2, "3"]ruby> ary[-2..-1][2, "3"]

    (Los ndices negativos indican que se empieza a contar desde el final del array, en vez del principio).Los arrays se pueden convertir a y obtener de cadenas utilizado join y split respectivamente:

    ruby> str = ary.join(:)"1:2:3"ruby> str.split(:)["1", "2", "3"]

    12

  • Captulo 7. HashesUn array asociativo contiene elementos que se pueden acceder, no a travs de ndices numricos secuenciales, sino atravs de claves que pueden tener cualquier tipo de valor. Estos arrays se conocen a veces como hash o diccionario;en el mundo Ruby se prefiere el trmino hash. Los hash se pueden crear mediante pares de elementos dentro de llaves({ }). Se usa la clave para encontrar algo en un hash de la misma forma que se utiliza el ndice para encontrar algo enun array.

    ruby> h = {1 => 2, "2" => "4"}{"2"=>"4", 1=>2}ruby> h[1]2ruby> h["2"]"4"ruby> h[5]nilruby> h[5] = 10 # aadimos un valor10ruby> h{5=>10, "2"=>"4", 1=>2}ruby> h[1]=nil # borramos un valornilruby> h[1]nilruby> h{5=>10, "2"=>"4", 1=>nil}

    13

  • Captulo 8. Retomando los ejemplos sencillosVamos ahora a desmontar el cdigo de nuestros anteriores programas ejemplo. Para que sirva de referencia vamos anumerar las lneas de todos los guiones.

    8.1. FactorialesEl siguiente guin aparece en el captulo Ejemplos sencillos.

    01 def fact(n)02 if n == 003 104 else05 n * fact(n-1)06 end07 end08 print fact(ARGV[0].to_i), "\n"

    Debido a que es la primera explicacin de un cdigo, vamos a ir lnea por lnea.

    01 def fact(n)

    En la primera lnea, def es la sentencia que define una funcin (o con mayor precisin, un mtodo; trataremos conms detalle los mtodos en un captulo posterior). Aqu se indica que la funcin fact toma un nico argumento, quese llama n.

    02 if n == 0

    Con if comprobamos una condicin. Si la condicin es cierta, se evala la siguiente lnea; si no independientementede lo que siga se evala el else

    03 1

    Si la condicin es cierta el valor del if es 1.

    04 else

    Si la condicin no es cierta, se evala el cdigo desde esta lnea hasta el end.

    05 n * fact(n - 1)

    Si no se satisface la condicin el valor de if es el resultado de multiplicar fact(n-1) por n.

    06 end

    El primer end cierra la sentencia if.

    14

  • Captulo 8. Retomando los ejemplos sencillos

    07 end

    El segundo end cierra la sentencia def.

    08 print fact(ARGV[0].to_i), "\n"

    Llama a la funcin fact() con el argumento especificado en la lnea de comandos, e imprime el resultado.ARGV es un array que contiene los argumentos de la lnea de comandos. Los miembros de ARGV son cadenas porlo que hay que convertirlos a nmeros enteros con to_i. Ruby no convierte automticamente las cadenas a nmeroscomo hace Perl.

    Hmmm... Qu pasa si alimentamos este programa con un nmero negativo? Se ve cul es el problema? Cmo sepodra solucionar?

    8.2. CadenasA continuacin examinaremos el programa acertijo del captulo sobre las cadenas

    01 words = [foobar, baz, quux]02 secret = words[rand(3)]0304 print "adivina? "05 while guess = STDIN.gets06 guess.chop!07 if guess == secret08 print "Ganas!\n"09 break10 else11 print "Lo siento. Pierdes\n"12 end13 print "adivina? "14 end15 print "La palabra era ", secret, ".\n"

    En este programa se utiliza una nueva estructura de control, while. El cdigo entre el while y su correspondiente endse ejecutar repetidamente mientras la condicin especificada se mantenga cierta.rand(3) de la lnea nmero 2 devuelve un nmero aleatorio dentro del rango de 0 a 2. Este nmero se utiliza paraextraer uno de los elementos del array words.

    En la lnea 5 se lee una lnea de la entrada estndar con el mtodo STDIN.gets. Si aparece el fin del fichero (EOF -End Of File), gets devuelve nil. Por lo tanto el cdigo asociado con el while se repetir hasta encontrar un ^D (o ^Zbajo DOS), que representa el fin de ficheroEn la lnea 6 guess.chop! elimina el ltimo carcter de guess; en este caso siempre el carcter de lnea nueva.

    En la lnea 15 se imprime la palabra secreta. Se ha escrito como una sentencia print con tres argumentos (que seimprimen uno detrs del otro), pero hubiera tenido la misma efectividad el hacerlo con un nico argumento escribiendosecret como #{secret} para resaltar que la variable se debe evaluar y no imprimir la palabra literal:

    print "la palabra era #{secret}. \n"

    15

  • Captulo 8. Retomando los ejemplos sencillos

    8.3. Expresiones regularesPor ltimo examinaremos el programa del captulo sobre expresiones regulares.

    01 st = "\033[7m"02 en = "\033[m"0304 while TRUE05 print "str> "06 STDOUT.flush07 str = gets08 break if not str09 str.chop!10 print "pat> "11 STDOUT.flush12 re = gets13 break if not re14 re.chop!15 str.gsub! re, "#{st}\\{en}"16 print str, "\n"17 end18 print "\n"

    En la lnea 4, se ha fijado la condicin del while a true con lo que se obtiene un bucle infinito. Sin embargo se hancolocado sentencias break en las lneas octava y decimotercera para salir del bucle. Estos dos breaks ejemplificantambin el uso de los modificadores if. Un modificador if ejecuta la sentencia del lado izquierdo si y slo si sesatisface la condicin especificada.

    Hay ms cosas que decir sobre chop! (veanse las lneas 9 y 14). En Ruby se aade, por convencin, ! o ? al finalde ciertos nombre de mtodos. El marca de exclamacin (!, pronunciada como un bang! sonoro) recalca algopotencialmente peligroso, es decir, algo que puede modificar el valor de lo que toca. chop! afecta directamente a lacadena pero chop sin el signo de exclamacin acta sobre una copia. A continuacin se muestra la diferencia.

    ruby> s1 = "forth""forth"ruby> s1.chop! # modifica s1"fort"ruby> s2 = s1.chop # sita en s2 una copia de la modificacin"for"ruby> s1 # ... sin afectar a s1"fort"

    Posteriormente no encontraremos con nombres de mtodos que finalizan con un signo de interrogacin (?, pronunci-ada a veces como un huh? sonoro). Esto indica que el mtodo es un predicado, aquel que puede devolver o trueo false.

    La lnea 15 requiere una especial atencin. Primero, se observa que gsub! es otro de los denominados mtodosdestructivos. Modifica str al reemplazar toda coincidencia del patrn re (sub significa sustituir, la g inicial indica quela sustitucin es global, es decir reemplaza todas las coincidencias que hay en la cadena no slo la primera encontrada).Hasta el momento todo parece correcto pero, Por qu reemplazamos las coincidencias del texto? st y en se definieronen las lneas 1 y 2 como secuencias ANSI que presentan el color del texto como invertido o normal respectivamente.En la lnea 15 se encuentran encerradas entre #{} para asegurar que se interpreten por lo que son (y no se impriman

    16

  • Captulo 8. Retomando los ejemplos sencillos

    los nombres de las variables). Entre ella se ve \\&. Esto es un pequeo truco. Dado que la sentencia de reemplazose encuentra entre comillas dobles, los dos backslashes se interpretarn como uno solo, que gsub! ver como \&que no es otra cosa que el cdigo que representa la primera coincidencia del patrn. Por lo tanto la nueva cadena, alimprimirse, ser igual que la antigua, excepto que las partes que coinciden con el patrn aparecen resaltadas en vdeoinverso.

    17

  • Captulo 9. Estructuras de controlEste captulo explora ms sentencias de control de Ruby.

    9.1. caseSe utiliza la sentencia case para comprobar una secuencia de condiciones. Superficialmente se parece al switch de Cy Java pero es considerablemente ms potente como veremos.

    ruby> i=88ruby> case iruby| when 1, 2..5ruby| print "1..5\n"ruby| when 6..10ruby| print "6..10\n"ruby| end6..10nil

    2..5 es una expresin que representa un rango entre 2 y 5 inclusive. La siguiente expresin verifica si el valor i caedentro del rango:

    (2..5) === i

    La sentencia case utiliza internamente el operador === para verificar las distintas condiciones. Dentro de la naturalezaorientada a objetos de Ruby, === lo interpreta el objeto que aparece en la condicin when. Por ejemplo, el cdigo quesigue comprueba en el primer when la igualdad de cadenas y en el segundo la coincidencia con una expresin regular.

    ruby> case abcdefruby| when aaa, bbbruby| print "aaa o bbb\n"ruby| when /def/ruby| print "incluye /def/\n"ruby| endincluye /def/nil

    9.2. whileRuby proporciona medios adecuados para la construccin de bucles, aunque veremos en el siguiente captulo que sise aprende a utilizar los iteradores a menudo hace innecesario su utilizacin explcita.

    18

  • Captulo 9. Estructuras de control

    Un while es un if repetido. Se ha utilizado en nuestros programas acertijo adivina-palabra y en las expresiones regu-lares (ver el captulo anterior); all tomaba la forma while condicin ... end que rodeaba el cdigo a repetir mientrasla condicin fuera cierta. Pero while e if se pueden aplicar fcilmente a sentencias individuales:

    ruby> i = 00ruby> print "Es cero.\n" if i == 0Es cero.nilruby> print "Es negativo\n" if i < 0nilruby> print "#{i+=1}\n" while i < 3123nil

    Algunas veces se necesita la condicin de comprobacin negada. Un unless es un if negado y un until es un whilenegado. Dejamos estas sentencias para que se experimente con ellas.Existen cuatro formas de interrumpir el progreso de un bucle desde su interior. La primera break, como en C, salecompletamente del bucle. La segunda next salta al principio de la siguiente iteracin del bucle (se corresponde conla sentencia continue del C). La tercera redo reinicia la iteracin en curso. A continuacin se muestra un extracto decdigo en C que ilustra el significado de break, next, y redo:

    while (condicion) {label_redo:goto label_next /*next*/goto label_break /*break*/goto label_redo /*redo*/;

    ;

    label_next:}label_break:;

    La cuarta forma de salir del interior de un bucle es return. La evaluacin de return provoca la salida no slo del buclesino tambin del mtodo que contiene el bucle. Si se le pasa un argumento, lo devolver como retorno de la llamadaal mtodo, si no el retorno ser nil.

    9.3. forLos programadores en C se estarn preguntando cmo se construye un bucle for. En Ruby, el bucle for es mucho msinteresante de lo que caba esperar. El siguiente bucle se ejecuta una vez por cada elemento de la coleccin.

    for elem in coleccion...

    end

    19

  • Captulo 9. Estructuras de control

    La coleccin puede ser un rango de valores (esto es lo que la mayora de la gente espera cuando se habla de buclesfor):

    ruby> for num in (4..6)ruby| print num, "\n"ruby| end4564..6

    Puede ser cualquier tipo de coleccin como por ejemplo un array:

    ruby> for elm in [100,-9.6,"pickle"]ruby| print "#{elm}\t(#{elm.type})\n"ruby| end100 (Fixnum)-9.6 (Float)pickle (String)[100, -9.6, "pickle"]

    Salindonos un poco del tema, for es realmente otra forma de escribir each, el cual es nuestro primer ejemplo deiterador. La siguientes dos estructuras son equivalentes:

    # Si utilizas C o Java, puedes preferir esta estructurafor i in coleccion

    ..

    end

    # si utilizas Smalltalk, puedes preferir esta otracoleccion.each {|i|

    ...

    }

    Con frecuencia se puede sustituir los bucles convencionales por iteradores y una vez acostumbrado a utilizarlos esgeneralmente ms sencillo tratar con stos. Por lo tanto, avancemos y aprendamos ms sobre ellos.

    20

  • Captulo 10. IteradoresLos iteradores no son un concepto original de Ruby. Son comunes en otros lenguajes orientados a objetos. Tambin seutilizan en Lisp aunque no se les conoce como iteradores. Sin embargo este concepto de iterador es muy poco familiarpara muchas personas por lo que se explorar con detalle.

    Como ya se sabe, el verbo iterar significa hacer la misma cosa muchas veces, por lo tanto un iterador es algo quehace la misma cosa muchas veces.

    Al escribir cdigo se necesitan bucles en diferentes situaciones. En C, se codifican utilizando for o while. Por ejemplo:

    char *str;for (str = "abcdefg"; *str != \0; str++) {

    /* aqu procesamos los caracteres */}

    La sintaxis del for(...) de C nos dota de una abstraccin que nos ayuda en la creacin de un bucle pero, la comprobacinde si *str es la cadena nula requiere que el programador conozca los detalles de la estructura interna de una cadena.Esto hace que C se parezca a un lenguaje de bajo nivel. Los lenguajes de alto nivel se caracterizan por un soporte msflexible a la iteracin. Consideremos el siguiente guin de la shell sh:

    #!/bin/sh

    for i in *.[ch]; do# ... aqu se hara algo con cada uno de los ficheros

    done

    Se procesaran todos los ficheros fuentes en C y sus cabeceras del directorio actual, el comando de la shell se encargarade los detalles de coger y sustituir los nombres de los ficheros uno por uno. Pensamos que este es un mtodo de trabajoa nivel superior que C, Verdad?

    Pero hay ms cosas a tener en cuenta: aunque est bien que un lenguaje tenga iteradores para todos los tipos de datosdefinidos en l, es decepcionante tener que volver a escribir bucles de bajo nivel para los tipos de datos propios. En laPOO, los usuarios definen sus propios tipos de datos a partir de otros, por lo tanto, esto puede ser un problema serio.

    Luego, todos los lenguajes OO incluyen ciertas facilidades de iteracin. Algunos lenguajes proporcionan clases espe-ciales con este propsito; Ruby nos permite definir directamente iteradores.

    El tipo strings de Ruby tiene algunos iteradores tiles:

    ruby> "abc".each_byte{|c| printf"{%c}", c}; print "\n"{a}{b}{c}nil

    each_byte es un iterador sobre los caracteres de una cadena. Cada carcter se sustituye en la variable local c. Esto sepuede traducir en algo ms parecido a C ...

    ruby> s="abc";i = 00ruby> while i < s.length

    21

  • Captulo 10. Iteradores

    ruby| printf "{%c}",s[i]; i+=1ruby| end; print "\n"{a}{b}{c}nil

    ... sin embargo el iterador each_byte es a la vez conceptualmente ms simple y tiene ms probabilidades de seguirfuncionando correctamente incluso cuando, hipotticamente, la clase string se modifique radicalmente en un futuro.Uno de los beneficios de los iteradores es que tienden a ser robustos frente a tales cambios, adems, sta es unacaracterstica del buen cdigo en general. (Si, tengamos paciencia tambin hablaremos de lo que son las clases)each_line es otro iterador de String.

    ruby> "a\nb\nc\n".each_line{|l| print l}a

    bc

    "a\nb\nc\n"

    Las tareas que ms esfuerzo llevan en C (encontrar los delimitadores de lnea, generar subcadenas, etc.) se evitanfcilmente utilizando iteradores.

    La sentencia for que aparece en captulos previos itera como lo hace el iterador each. El iterador each de Stringfunciona de igual forma que each_line, reescribamos ahora el ejemplo anterior con un for:

    ruby> for l in "a\nb\nc\n"ruby| print lruby| enda

    bc

    "a\nb\nc\n"

    Se puede utilizar la sentencia de control retry junto con un bucle de iteracin y se repetir la iteracin en curso desdeel principio.

    ruby> c = 00ruby> for i in 0..4ruby| print iruby| if i == 2 and c == 0ruby| c = 1ruby| print "\n"ruby| retryruby| endruby| end; print "\n"01201234nil

    A veces aparece yield en la definicin de un iterador. yield pasa el control al bloque de cdigo que se pasa al iterador(esto se explorar con ms detalle es el captulo sobre los objetos procedimiento). El siguiente ejemplo define eliterador repeat, que repite el bloque de cdigo el nmero de veces especificado en el argumento.

    22

  • Captulo 10. Iteradores

    ruby> def repeat(num)ruby| while num > 0ruby| yieldruby| num -= 1ruby| endruby| endnilruby> repeat(3){ print "foo\n" }foofoofoonil

    Con retry se puede definir un iterador que funciona igual que while, aunque es demasiado lento para ser prctico.

    ruby> def WHILE(cond)ruby| return if not condruby| yieldruby| retryruby| endnilruby> i=0;WHILE(i

  • Captulo 11. Pensamiento orientado a objetosLa orientacin a objetos es una palabra con gancho. Llamar a cualquier cosa orientada a objetos puede hacerlaparecer ms elegante. Ruby reclama ser un lenguaje de guiones orientado a objetos: pero, Qu significa exactamenteorientado a objetos?Existe una gran variedad de respuestas a esta pregunta, y probablemente todas ellas se pueden reducir a la misma cosa.En vez de recapitular demasiado deprisa, pensemos un momento en el paradigma de la programacin tradicional.

    Tradicionalmente, un problema informtico se ataca produciendo algn tipo de representacin de datos y proced-imientos que operan sobre esos datos. Bajo este modelo, los datos son inertes, pasivos e incapaces. Estn a la completamerced de un gran cuerpo procedimental, que es activo, lgico y todopoderoso.

    El problema con esta aproximacin es, que los programas los escriben programadores, que son humanos, que slopueden retener cierto nmero de detalles en sus cabezas en un momento determinado. A medida que crece el proyecto,el ncleo procedimental crece hasta un punto que se hace difcil recordar cmo funciona todo el conjunto. Pequeoslapsos de pensamiento o errores tipogrficos llegan a ser errores muy ocultos. Empiezan a surgir interacciones com-plejas e inintencionadas dentro de este ncleo y el mantenimiento se convierte en algo parecido a transportar uncalamar gigante intentado que ninguno de sus tentculos te alcance la cara. Existen polticas de programacin queayudan a minimizar y localizar errores dentro de este paradigma tradicional pero existe una solucin mejor que pasafundamentalmente por cambiar la forma de trabajar.Lo que hace la programacin orientada a objetos es, delegar la mayora del trabajo mundano y repetitivo a los propiosdatos; modifica el concepto de los datos que pasan de pasivos a activos. Dicho de otra forma.

    Dejamos de tratar cada pieza de dato como una caja en la que se puede abrir su tapa y arrojar cosas en ella. Empezamos a tratar cada pieza de dato como una mquina funcional cerrada con unos pocos interruptores y diales

    bien definidos.

    Lo que se define anteriormente como una mquina puede ser, en su interior, algo muy simple o muy complejo. Nose puede saber desde el exterior y no se nos permite abrir la mquina (excepto cuando estamos completamente segurosde que algo est mal en su diseo), por lo que se nos obliga a conmutar interruptores y leer los diales para interactuarcon los datos. Una vez construida, no queremos tener que pensar en como funciona internamente.

    Se podra pensar que estamos haciendo ms trabajo nosotros mismos, pero esta forma de trabajo tiende a ser un buenmtodo para evitar que vayan mal todo tipo de cosas.

    Comencemos con un ejemplo que es demasiado simple para tener algn valor prctico pero que al menos muestraparte del concepto. Nuestro coche consta de un odmetro. Su trabajo consiste en llevar un registro de la distanciarecorrida desde la ltima vez que se puls el botn de reinicializacin. Cmo podramos representar esto en unlenguaje de programacin? En C, el odmetro sera, simplemente, una variable numrica de tipo float. El programamanipulara esa variable aumentando el valor en pequeos incrementos y ocasionalmente la reinicializara a cerocuando fuese apropiado. Qu hay de malo en esto? Un error en el programa podra asignar un valor falso a lavariable, por cualquier nmero de razones inesperadas. Cualquiera que haya programado en C sabe que se puedenperder horas o das tratando de encontrar ese error que una vez encontrado parece absurdamente simple. (El momentode encontrar el error es indicado por una sonora palmada en la frente).En un contexto orientado a objetos, el mismo problema se puede atacar desde un ngulo completamente diferente. Laprimera cosa que se pregunta un programador al disear el odmetro no es qu tipos de datos son los ms cercanospara representar esta cosa? sino cmo se supone que acta esta cosa?. La diferencia termina siendo profunda.Es necesario dedicar cierto tiempo a decidir para qu es exactamente un odmetro y cmo se espera que el mundo

    24

  • Captulo 11. Pensamiento orientado a objetos

    exterior interacte con el. Se decide entonces construir una pequea mquina con controles que permitan incrementar,reinicializar y leer su valor y nada ms.

    El odmetro se crea sin un mecanismo para asignarle un valor arbitrario, Por qu? porque es de todos sabido que losodmetros no trabajan de esa forma. Existen slo unas cuantas cosas que un odmetro puede hacer, y slo permitimosesas cosas. As, si alguien desde un programa trata de asignar algn otro valor (por ejemplo, la temperatura lmitedel sistema de control de climatizacin del vehculo) al odmetro, aparece de inmediato una indicacin de lo que vamal. Al ejecutar el programa se nos dice (o posiblemente, al compilarlo dependiendo de la naturaleza del lenguaje)que No se nos permite asignar valores arbitrarios al objeto Odmetro. El mensaje podra ser menos preciso, pero srazonablemente prximo al problema. Esto no evita el error, verdad? pero apunta rpidamente en la direccin de lacausa. Esta es slo alguna de mltiples formas en las que la programacin OO nos puede evitar muchas prdidas detiempo.

    Existe, normalmente, un nivel de abstraccin superior a ste porque resulta que es igual de fcil construir una factoraque hace mquinas como hacer una mquina individual. Es poco probable que construyamos un nico odmetro, sinoque nos preparamos para construir cualquier cantidad de odmetros a partir de un nico patrn. El patrn (o si losprefieres, la factora de odmetros) es lo que se conoce como clase y el odmetro individual sacado del patrn (oconstruido en la factora) se conoce como objeto. La mayora de los lenguajes OO necesitan una clase para tener unnuevo tipo de objeto pero Ruby no.Conviene resaltar aqu que la utilizacin de un lenguaje OO no obliga a un diseo OO vlido. Es posible, en cualquierlenguaje, escribir cdigo poco claro, descuidado, mal concebido, errneo e inestable. Lo que permite Ruby (en oposi-cin, especialmente, a C++) es que la prctica de la programacin OO sea lo suficientemente natural para que, incluso,trabajando a pequea escala no se sienta la necesidad de recurrir a un cdigo mal estructurado por evitar esfuerzo. Setratar la forma en que Ruby logra este admirable propsito a medida que avancemos en esta gua; el prximo temasern los interruptores y diales (mtodos del objeto) y a partir de aqu pasaremos a las factoras (clases). Siguescon nosotros?

    25

  • Captulo 12. MtodosQu es un mtodo? En la programacin OO no se piensa en operar sobre los datos directamente desde el exteriorde un objeto; si no que los objetos tienen algn conocimiento de cmo se debe operar sobre ellos (cuando se lespide amablemente). Podramos decir que se pasa un mensaje al objeto y este mensaje obtiene algn tipo de accin orespuesta significativa. Esto debe ocurrir sin que tengamos necesariamente algn tipo de conocimiento o nos importecomo realiza el objeto, interiormente, el trabajo. Las tareas que podemos pedir que un objeto realice (o lo que es lomismo, los mensajes que comprende) son los mtodos.En Ruby, se llama a un mtodo con la notacin punto (como en C++ o Java). El objeto con el que nos comunicamosse nombra a la izquierda del punto.

    ruby> "abcdef".length6

    Intuitivamente, a este objeto cadena se le est pidiendo que diga la longitud que tiene. Tcnicamente, se est llamandoal mtodo length del objeto "abcdef".Otros objetos pueden hacer una interpretacin un poco diferente de length. La decisin sobre cmo responder a unmensaje se hace al vuelo, durante la ejecucin del programa, y la accin a tomar puede cambiar dependiendo de lavariable a que se haga referencia.

    ruby> foo = "abc""abc"ruby> foo.length3ruby> foo = ["abcde","fghij"]["abcde", "fghij"]ruby> foo.length2

    Lo que indicamos con length puede variar dependiendo del objeto con el que nos comunicamos. En el primer ejemplole pedimos a foo su longitud, como referencia a una cadena simple, slo hay una respuesta posible. En el segundoejemplo, foo referencia a un array, podramos pensar que su longitud es 2, 5 10; pero la respuesta ms plausible es2 (los otros tipos de longitud se podran obtener si se desea)

    ruby> foo[0].length5ruby> foo[0].length + foo[1].length10

    Lo que hay que tener en cuenta es que, el array conoce lo que significa ser un array. En Ruby, las piezas de datosllevan consigo ese conocimiento por lo que las solicitudes que se les hace se pueden satisfacer en las diferentes formasadecuadas. Esto libera al programador de la carga de memorizar una gran cantidad de nombres de funciones, ya queuna cantidad relativamente pequea de nombre de mtodos, que corresponden a conceptos que sabemos como expresaren lenguaje natural, se pueden aplicar a diferentes tipos de datos siendo el resultado el que se espera. Esta caractersticade los lenguajes OO (que, IMHO1, Java ha hecho un pobre trabajo en explotar), se conoce como polimorfismoCuando un objeto recibe un mensaje que no conoce, salta un error:

    26

  • Captulo 12. Mtodos

    ruby> foo = 55ruby> foo.lengthERR: (eval):1: undefined method length for 5:Fixnum

    Por lo tanto hay que conocer qu mtodos son aceptable para un objeto, aunque no se necesita saber como sonprocesados.

    Si se pasan argumentos a un mtodo, stos van normalmente entre parntesis.

    objeto.metodo(arg1, arg2)

    pero se pueden omitir, si su ausencia no provoca ambigedad

    objeto.metodo arg1, arg2

    En Ruby existe una variable especial self que referencia al objeto que llama a un mtodo. Ocurre con tanta frecuenciaque por conveniencia se omite en las llamadas de un mtodo dentro de un objeto a sus propios mtodos:

    self.nombre_de_metodo(args ...)

    es igual que:

    nombre_de_metodo(args ...)

    Lo que conocemos tradicionalmente como llamadas a funciones es esta forma abreviada de llamar a un mtodo atravs de self. Esto hace que a Ruby se le conozca como un lenguaje orientado a objetos puro. An as, los mtodosfuncionales se comportan de una forma muy parecida a las funciones de otros lenguajes de programacin en beneficiode aquellos que no asimilen que las llamadas a funciones son realmente llamadas a mtodos en Ruby. Se puede hablarde funciones como si no fuesen realmente mtodos de objetos, si queremos.

    Notas1. In My Honest Opinion (En mi sincera opinin)

    27

  • Captulo 13. ClasesEl mundo real est lleno de objetos que podemos clasificar. Por ejemplo, un nio muy pequeo es probable que digaguau guau cuando vea un perro, independientemente de su raza; naturalmente vemos el mundo en base a estascategoras.

    En terminologa OO, una categora de objetos, como perro, se denomina clase y cualquier objeto determinado quepertenece a una clase se conoce como instancia de esa clase.

    Generalmente, en Ruby y en cualquier otro lenguaje OO, se define primero las caractersticas de una clase, luego secrean las instancias. Para mostrar el proceso, definamos primero una clase muy simple Perro.

    ruby> class Perroruby| def ladraruby| print "guau guau\n"ruby| endruby| endnil

    En Ruby, la definicin de una clase es la regin de cdigo que se encuentra entre las palabras reservadas class y end.Dentro de esta rea, def inicia la definicin de un mtodo, que como se dijo en el captulo anterior, corresponde conalgn comportamiento especfico de los objetos de esa clase.Ahora que tenemos definida la clase Perro, vamos a utilizarla:

    ruby> rufi = Perro.new#

    Hemos creado una instancia nueva de la clase Perro y le hemos llamado rufi. El mtodo new de cualquier clase, creauna nueva instancia. Dado que rufi es un Perro, segn la definicin de la clase, tiene las propiedades que se decidique un Perro deba tener. Dado que la idea de Perrunidad es muy simple, slo hay una cosa que puede hacer rufi

    ruby> rufi.ladraguau guaunil

    La creacin de una instancia de una clase se conoce, a veces, como instanciacin. Es necesario tener un perro antes deexperimentar el placer de su conversacin; no se puede pedir simplemente a la clase Perro que ladre para nosotros:

    ruby> Perro.ladraERR: (eval):1: undefined method ladra for Perro:Class

    Tiene el mismo sentido que intentar comer el concepto de un sndwich

    Por otro lado, si queremos or el sonido de un perro sin estar emocionalmente atados, podemos crear (instanciar) unperro efmero, temporal y obtener un pequeo sonido antes de que desaparezca.

    ruby> (Perro.new).ladra # o tambin, Perro.new.ladraguau guaunil

    28

  • Captulo 13. Clases

    Pero un momento, qu es todo esto de que a continuacin el pobre tipo desaparece?. Pues es verdad, si no nospreocupamos de darle un nombre (como hicimos con rufi) el recolector de basura automtico de Ruby decide que setrata de un perro perdido, no deseado y sin piedad se deshace de l. Ciertamente est muy bien, porque podemos creartodos los perros que queramos.

    29

  • Captulo 14. HerenciaLa clasificacin de los objetos en nuestra vida diaria es evidentemente jerrquica. Sabemos que todos los gatos sonmamferos y que todos los mamferos son animales. Las clases inferiores heredan caractersticas de las clases superi-ores a las que pertenecen. Si todos los mamferos respiran, entonces los gatos respiran.

    Este concepto se puede expresar en Ruby:

    ruby> class Mamiferoruby| def respiraruby| print "inhalar y exhalar\n"ruby| endruby| endnilruby> class Gato tama = Gato.new#ruby> tama.respirainhalar y exhalarnilruby> tama.maullamiaunil

    Existen situaciones donde ciertas propiedades de las superclases no deben heredarse por una determinada subclase.Aunque en general los pjaros vuelan, los pinginos es una subclase de los pjaros que no vuelan.

    ruby> class Pajaroruby| def aseoruby| print "me estoy limpiando las plumas."ruby| endruby| def vuelaruby| print "estoy volando."ruby| endruby| endnilruby> class Pinguino

  • Captulo 14. Herencia

    ruby| endruby| endnil

    En vez de definir exhaustivamente todas las caractersticas de cada nueva clase, lo que se necesita es aadir o re-definir las diferencias entre cada subclase y superclase. Esta utilizacin de la herencia se conoce como programacindiferencial. Y es uno de los beneficios de la programacin orientada a objetos.

    31

  • Captulo 15. Redefinicin de mtodosEn una subclase se puede modificar el comportamiento de las instancias redefiniendo los mtodos de la superclase.

    ruby> class Humanoruby| def identidadruby| print "soy una persona.\n"ruby| endruby| def tarifa_tren(edad)ruby| if edad < 12ruby| print "tarifa reducida.\n"ruby| elseruby| print "tarifa normal. \n"ruby| endruby| endruby| endnilruby> Humano.new.identidadsoy una persona.nilruby> class Estudiante Estudiante.new.identidadsoy un estudiante.nil

    Supongamos que en vez de reemplazar el mtodo identidad lo que queremos es mejorarlo. Para ello podemos utilizarsuper

    ruby> class Estudiante2 Estudiante2.new.identidadsoy una persona.tambin soy un estudiante.nil

    super nos permite pasar argumentos al mtodo original. Se dice que hay dos tipos de personas ...

    ruby> class Deshonesta

  • Captulo 15. Redefinicin de mtodos

    ruby| endruby| endnilruby> Deshonesta.new.tarifa_tren(25)tarifa reducida.nilruby> class Honesta Honesta.new.tarifa_tren(25)tarifa normal.nil

    33

  • Captulo 16. Control de accesosSe ha dicho anteriormente que Ruby no tiene funciones, slo mtodos. Sin embargo existe ms de una clase demtodos. En esta captulo vamos a presentar el control de accesos.

    Vamos a considerar lo que pasa cuando se define un mtodo en el nivel superior, no dentro de una clase. Se puedepensar que dicho mtodo es anlogo a una funcin de un lenguaje ms tradicional como C.

    ruby> def square(n)ruby| n * nruby| endnilruby> square(5)25

    Nuestro nuevo mtodo parece que no pertenece a ninguna clase, pero de hecho Ruby se lo asigna a la clase Object,que es la superclase de cualquier otra clase. Como resultado de esto cualquier objeto es capaz de utilizar este mtodo.Esto es cierto, pero existe un pequeo pero; es un mtodo privado a cada clase. A continuacin hablaremos ms de loque esto significa, pero una de sus consecuencias es que slo se puede llamar de la siguiente forma

    ruby> class Fooruby| def fourth_power_of (x)ruby| square(x) * square(x)ruby| endruby| endnilruby> Foo.new.fourth_power_of 1010000

    No se nos permite aplicar explcitamente el mtodo a un objeto:

    "fish".square(5)ERR: (eval):1: private method square called for "fish":String

    Esto preserva con inteligencia la naturaleza puramente OO de Ruby (las funciones siguen siendo mtodos de objetos,donde el receptor implcito es self), a la vez que proporciona funciones que se pueden escribir de igual forma que enlenguajes tradicionales.Una disciplina mental comn en la programacin OO, que ya se seal en un captulo anterior, tiene que ver con laseparacin de la especificacin y la implementacin o qu tareas se supone que un objeto realiza y cmo realmentese consiguen. El trabajo interno de un objeto debe mantenerse, por lo general, oculto a sus usuarios; slo se tiene quepreocupar de lo que entra y lo que sale y confiar en que el objeto sabe lo que est realizando internamente. As, esgeneralmente til que las clases posean mtodos que el mundo exterior no ve, pero que se utilizan internamente (y quepueden ser mejorados por el programador cuando desee, sin modificar la forma en que los usuarios ven los objetos deesa clase). En el trivial ejemplo que sigue, pinsese que engine es el motor interno de la clase.

    ruby> class Testruby| def times_two(a)ruby| print a," dos veces es ",engine(a),"\n"ruby| end

    34

  • Captulo 16. Control de accesos

    ruby| def engine(b)ruby| b*2ruby| endruby| private:engine # esto oculta engine a los usuariosruby| endTestruby> test = Test.new#ruby> test.engine(6)ERR: (eval):1: private method engine called for #ruby> test.times_two(6)6 dos veces es 12nil

    Se podra esperar que test.engine(6) devolviese 12, pero por el contrario se nos comunica que engine es inaccesiblecuando actuamos como usuario del objeto Test. Slo otros mtodos de Test, como times_two tienen permiso parautilizar engine. Se nos obliga a pasar por el interfaz pblico, que es el mtodo times_two. El programador que estal cargo de la clase puede modificar engine (en este caso cambiando b*2 por b+b suponiendo que as mejora elrendimiento) sin afectar cmo los usuarios interactan con el objeto Test. Este ejemplo, por supuesto, es demasiadosimple para ser til; los beneficios de control de accesos se manifiestan cuando se comienzan a crear clases mscomplicadas e interesantes.

    35

  • Captulo 17. Mtodos singletonEl comportamiento de una instancia viene determinado por su clase, pero hay veces que sabemos que una determinadainstancia debe tener un comportamiento especial. En la mayora de los lenguajes debemos meternos en la problemticade crear otra clase e instanciarla slo una vez. En Ruby se puede asignar a cada OBJETO sus propios mtodos.

    ruby> class SingletonTestruby| def sizeruby| print "25\n"ruby| endruby| endnilruby> test1 = SingletonTest.new#ruby> test2 = SingletonTest.new#ruby> def test2.sizeruby| print "10\n"ruby| endnilruby> test1.size25nilruby> test2.size10nil

    En este ejemplo, test1 y test2 pertenecen a la misma clase, pero a test2 se le ha redefinido el mtodo size y por lotanto se comportan de forma diferente. Un mtodo que pertenece slo a un objeto se conoce como mtodo singleton.Los mtodos singleton se utilizan frecuentemente en los elementos de un interfaz grfico de usuario (GUI1) cuando sedeben realizar acciones diferentes cuando se pulsan botones diferentes.

    Los mtodos singleton no son nicos de Ruby, aparecen tambin en CLOS, Dylan, etc. Otros lenguajes como porejemplo Self y NewtonScript, slo tienen mtodos singleton. A estos se les conoce como lenguajes basados en pro-totipos

    Notas1. Graphical User Interface

    36

  • Captulo 18. MdulosLos mdulos en Ruby son similares a las clases, excepto en:

    Un mdulo no puede tener instancias

    Un mdulo no puede tener subclases

    Un mdulo se define con module ... end

    Ciertamente ... la clase Module de un mdulo es la superclase de la clase Class de una clase. Se pilla esto? No?Sigamos.

    Existen dos usos tpicos de los mdulos. Uno es agrupar mtodos y constantes relacionadas en un repositorio central.El mdulo Math de Ruby hace esta funcin:

    ruby> Math.sqrt(2)1.414213562ruby> Math::PI3.141592654

    El operador :: indica al intrprete de Ruby qu mdulo debe consultar para obtener el valor de la constante (esconcebible, que algn otro mdulo a parte de Math interprete PI de otra forma). Si queremos referenciar a los mtodoso constantes de un mdulo, directamente, sin utilizar ::, podemos incluir ese mdulo con include:

    ruby> include MathObjectruby> sqrt(2)1.414213562ruby> PI3.141592654

    El otro uso de los mdulos se denomina mixin. Algunos lenguajes OO, incluidos el C++, permiten herencia mltiple,es decir, una clase puede heredar de ms de una superclase. Un ejemplo de herencia mltiple en el mundo real es undespertador, se podra pensar que un despertador es una clase de reloj y que tambin pertenece a la clase de objetosque podramos llamar zumbadores.

    Ruby, con toda la intencin del mundo, no implementa herencia mltiple real, aunque la tcnica de los mixins es unabuena alternativa. Recurdese que los mdulos no se pueden instanciar ni se pueden crear subclases de ellos; pero sise incluye un mdulo en la definicin de una clase sus mtodos quedan aadidos a ella, es decir se asocian (mixin1) ala clase.

    Se puede pensar que los mixins son una forma de pedir qu propiedades concretas se desean. Por ejemplo, si unaclase tiene un mtodo each funcional, asociarla con el mdulo Enumerable de la biblioteca estndar nos proporcionagratuitamente los mtodos sort y find.

    Esta utilizacin de los mdulos proporciona la funcionalidad bsica de la herencia mltiple permitindonos representarlas relaciones de la clase en una simple estructura en rbol que simplifica considerablemente la implementacin dellenguaje (Los diseadores de Java hicieron una eleccin parecida).

    37

  • Captulo 18. Mdulos

    Notas1. asociar

    38

  • Captulo 19. Objetos procedimientoA menudo es deseable tener la posibilidad de definir respuestas especficas a sucesos inesperados. Resulta que estose consigue con gran sencillez si podemos pasar un bloque de cdigo a otros mtodos, lo que significa que deseamostratar el cdigo como si fuesen datos.

    Un objeto procedimiento nuevo se obtiene utilizando proc:

    ruby> quux = proc {ruby| print "QUUXQUUXQUUX!!!\n"ruby| }#

    Ahora quux referencia a un objeto y como las mayora de los objetos, tiene un comportamiento que se puede invocar.Concretamente, podemos pedir que se ejecute a travs de su mtodo call

    ruby> quux.callQUUXQUUXQUUX!!!nil

    Luego, despus de todo esto. Podemos utilizar quux cmo un argumento de un mtodo? Ciertamente.

    ruby> def run ( p )ruby| print "Vamos a llamar a un procedimiento ... \n"ruby| p.callruby| print "Finalizado. \n"ruby| endnilruby> run quuxVamos a llamar a un procedimiento ...QUUXQUUXQUUX!!!Finalizado.nil

    El mtodo trap nos permite asignar una respuesta personalizada a cualquier seal del sistema.

    ruby> inthandler = proc{ print "^C ha sido pulsado.\n" }#ruby> trap "SIGINT", inthandlernil

    Normalmente, al pulsar ^C se sale del intrprete. Ahora se imprime un mensaje y el intrprete sigue ejecutndose, asno se pierde el trabajo realizado. (No nos encontramos atrapados en el intrprete para siempre; todava se puede salirtecleando exit o pulsando ^D.)Una observacin final antes de pasar a otros temas: no es necesario dar al objeto procedimiento un nombre antes deasociarlo a una seal. Un objeto procedimiento annimo equivalente se asemejara a:

    ruby> trap "SIGINT", proc{ print "^C ha sido pulsado.\n" }#

    39

  • Captulo 19. Objetos procedimiento

    O de una forma ms compacta todava,

    ruby> trap "SIGINT", print "^C ha sido pulsado.\n"#

    Este formato abreviado es mas adecuado y legible cuando se escriben pequeos procedimientos annimos.

    40

  • Captulo 20. VariablesRuby tiene tres clases de variables, una clase de constante y exactamente dos pseudo-variables. Las variables y lasconstantes no tienen tipo. Aunque las variables sin tipo tienen sus inconvenientes, presentan ms ventajas y se adaptanmejor a la filosofa rpido y sencillo de RubyEn la mayora de los lenguajes hay que declarar las variables para especificar su tipo, si se pueden modificar (e.g. sison constantes) e indicar su mbito, ya que no es ningn problema el tipo y como vamos a ver, el resto se obtiene apartir del nombre, en Ruby no se necesita declarar las variables.

    El primer carcter de un identificador lo cataloga de un plumazo:

    Tabla 20-1. Clases de variables

    $ Variable global@ Variable instancia[a-z] _ Variable local[A-Z] Constante

    Las nicas excepciones a lo expuesto en la tabla son las pseudo-variables de Ruby: self, que referencia al objeto queest en ese momento en ejecucin y nil que es el valor nulo que toman las variables no inicializadas. Ambos tienenun identificador como de variable local pero self es una variable global que la mantiene el interprete y nil es unaconstante. Como estas son las nicas excepciones no provocan mucha confusin.

    No se debe asignar valores a self y nil principalmente porque un valor de self referencia al objeto de nivel superior:

    ruby> selfmainruby> nilnil

    20.1. Variables globalesUna variable global tiene un nombre que comienza con $. Se puede utilizar en cualquier parte de un programa. Antesde inicializarse, una variable global tiene el valor especial nil.

    ruby> $foonilruby> $foo = 55ruby> $foo5

    La variables globales deben utilizarse con parquedad. Son peligrosas porque se pueden modificar desde cualquierlugar. Una sobreutilizacin de variables globales puede dificultar la localizacin de errores; tambin indica que no

    41

  • Captulo 20. Variables

    se ha pensado detenidamente el diseo del programa. Siempre que se encuentre la necesidad de utilizar una variableglobal, hay que darle un nombre descriptivo para que no se pueda utilizar inadvertidamente para otra cosa (Llamarle$foo como se ha hecho en el ejemplo es probablemente una mala idea)Una caracterstica notable de las variables globales es que se pueden trazar; se puede definir un procedimiento que sellame cada vez que se modifique el valor de la variable.

    ruby> trace_var:$x, proc{print "$x es ahora ", $x, "\n"}nilruby> $x = 5$x es ahora 55

    Cuando una variable global se la atava para que funcione con un disparador que se llama cada vez que se modifica,se la conoce como variable activa. Son tiles, por ejemplo, para mantener un GUI actualizado.Existe un grupo especial de variables cuyos nombres constan del smbolo del dolar ($) seguido de un carcter. Porejemplo, $$ contiene el nmero de identificacin del proceso del intrprete de Ruby, y es de slo lectura. A contin-uacin se muestran las principales variables del sistema y su significado (acudir al manual de referencia de Rubypara ms detalles)

    Tabla 20-2. Variables de sistema

    $! ltimo mensaje de error$@ Posicin del error$_ ltima cadena leda con gets$. ltimo nmero de lnea ledo por el interprete$& ltima cadena que ha coincidido con una expresin

    regular$~ ltima cadena que ha coincidido con una expresin

    regular como array de subexpresiones$n La n-sima subexpresin regular de la ltima

    coincidencia (igual que $~[n])$= flag para tratar igual las maysculas y minsculas$/ Separador de registros de entrada$\ Separador de registros de salida$0 El nombre del fichero del guin Ruby$* El comando de la lnea de argumentos$$ El nmero de identificacin del proceso del intrprete

    Ruby$? Estado de retorno del ltimo proceso hijo ejecutado

    De las variables anteriores $_ y $~, tienen mbito local. Sus nombres sugieren que deberan tener mbito global, peroson ms tiles de esta forma y existen razones histricas para utilizar estos identificadores.

    42

  • Captulo 20. Variables

    20.2. Variables de instanciaUna variable de instancia tiene un nombre que comienza con @ y su mbito est limitado al objeto al que referenciaself. Dos objetos diferentes, an cuando pertenezcan a la misma clase, pueden tener valores diferentes en sus variablesde instancia. Desde el exterior del objeto, las variables de instancia, no se pueden alterar e incluso, no se puedenobservar (es decir, en Ruby las variables de instancia nunca son pblicas) a excepcin de los mtodos proporcionadosexplcitamente por el programador. Como con las variables globales, las variables de instancia tienen el valor nil antesde que se inicialicen

    Las variables de instancia en Ruby no necesitan declararse. Esto da lugar a una estructura flexible de los objetos. Dehecho, cada variable de instancia se aade dinmicamente al objeto la primera vez que se la referencia

    ruby> class InstTestruby| def set_foo(n)ruby| @foo = nruby| endruby| def set_bar(n)ruby| @bar = nruby| endruby| endnilruby> i = InstTest.new#ruby> i.set_foo(2)2ruby> i#ruby> i.set_bar(4)4ruby> i#

    Obsrvese que i no informa del valor de @bar hasta que no se haya llamado al mtodo set_bar

    20.3. Variables localesUna variable local tiene un nombre que empieza con una letra minscula o con el carcter de subrayado (_). Lasvariables locales no tienen, a diferencia de las variables globales y las variables de instancia, el valor nil antes de lainicializacin:

    ruby> $foonilruby> @foonilruby> fooERR: (eval):1: undefined local variable or method foo for #

    La primera asignacin que se realiza sobre una variable local acta como una declaracin. Si se referencia a unavariable local no inicializada, el intrprete de Ruby piensa que se trata de una llamada a un mtodo con ese nombre;de ah el mensaje de error del ejemplo anterior.

    43

  • Captulo 20. Variables

    Generalmente el mbito de una variable local es uno de los siguientes:

    proc{ ... } loop{ ... } def ... end

    class ... end

    module ... end

    Todo el programa (si no es aplicable ninguno de los puntos anteriores)

    En el siguiente ejemplo define? es un operador que verifica si un identificador est definido. Si lo est, devuelve unadescripcin del mismo, en caso contrario, devuelve nil. Como se ve el mbito de bar es local al bucle, cuando se saledel bucle, bar est sin definir.

    ruby> foo =44; print foo, "\n"; defined? foo44"local-variable"ruby> loop{bar = 45;print bar, "\n"; break}; defined? var45nil

    Los objetos procedimiento que residen en el mismo mbito comparten las variables locales que pertenecen a esembito. En el siguiente ejemplo, la variable bar es compartida por main y los objetos procedimiento p1 y p2:

    ruby> bar=00ruby> p1 = proc{|n| bar = n}#ruby> p2 = proc{bar}#ruby> p1.call(5)5ruby> bar5ruby> p2.call5

    Obsrvese que no se puede omitir la lnea bar=0 inicial; esta asignacin es la que garantiza que el mbito de barincluir a p1 y p2. Si no, p1 y p2 tendrn al final cada uno su propia variable local bar y la llamada a p2 dar lugar aun error de variable o mtodo no definido.

    Una caracterstica muy poderosa de los objetos procedimiento se deriva de su capacidad para recibir argumentos; lasvariables locales compartidas permanecen vlidas incluso cuando se las pasa fuera de su mbito original.

    ruby> def boxruby| contents = 15ruby| get = proc{contents}ruby| set = proc{|n| contents = n}

    44

  • Captulo 20. Variables

    ruby| return get, setruby| endnilruby> reader, writer = box[#, #]ruby> reader.call15ruby> writer.call(2)2ruby> reader.call2

    Ruby es especialmente inteligente con respecto al mbito. En el ejemplo, es evidente que la variable contents estcompartida por reader y writer. Ahora bien, es posible definir varios pares reader-writer que utilicen box cada unode los cuales comparten su propia variable contents sin interferir uno con otro.

    ruby> reader_1, writer_1 = box[#, #]ruby> reader_2, writer_2 = box[#, #]ruby> writer_1.call(99)99ruby> reader_1.call99ruby> reader_2.call15

    45

  • Captulo 21. ConstantesUna constante tiene un nombre que comienza con una letra mayscula. Se le debe asignar valor slo una vez. En laimplementacin actual de Ruby, reasignar un valor a una constante genera un aviso y no un error (la versin no ANSIde eval.rb no informa de este aviso):

    ruby> fluid = 3030ruby> fluid = 3131ruby> Solid = 3232ruby> Solid = 33(eval):1: warning: already initialized constant Solid33

    Las constantes se pueden definir en una clase, pero a diferencia de las variables de instancia, son accesibles desde elexterior de la misma.

    ruby> class ConstClassruby| C1=101ruby| C2=102ruby| C3=103ruby| def showruby| print C1," ",C2, " ",C3,"\n"ruby| endruby| endnilruby> C1ERR: (eval):1: uninitialized constant C1ruby> ConstClass::C1101ruby> ConstClass.new.show101 102 103nil

    Las constantes tambin se pueden definir en un mdulo.

    ruby> module ConstModuleruby| C1=101ruby| C2=102ruby| C3=103ruby| def showConstantsruby| print C1," ",C2," ",C3,"\n"ruby| endruby| endnilruby> C1ERR: (eval):1: uninitialized constant C1

    46

  • Captulo 21. Constantes

    ruby> include ConstModuleObjectruby> C1101ruby> showConstants101 102 103nilruby> C1=99 # realmente una idea no muy buena99ruby> C199ruby> ConstModule::C1 # La constante del mdulo queda sin tocar ...101ruby> ConstModule::C1=99ERR: (eval):1: compile error(eval):1: parse errorConstModule::C1=99

    ^

    ruby> ConstModule::C1 #... independientemente de lo que hayamos jugado con ella101

    47

  • Captulo 22. Procesamiento de excepciones:rescue

    Un programa en ejecucin puede encontrarse con problemas inesperados. Podra no existir un fichero que desea leer,al salvar algunos datos se podra llenar un disco, un usuario podra introducir algn tipo de datos de entrada pocoadecuados.

    ruby> file = open("algun_fichero")ERR: (eval):1:in open: No such file or directory - "algun_fichero"

    Un programa robusto manejar estas situaciones prudente y elegantemente. Satisfacer estas expectativas puede seruna tarea exasperante. Los programadores en C se supone que deben verificar toda llamada al sistema que pudiesefallar y decidir inmediatamente que hacer.

    FILE *file = fopen("algun_fichero","r");if (file == NULL) {

    fprintf(stderr, "No existe el fichero\n");exit(1);

    }bytes_read = fread(buf,1,bytes_desired,file);if (bytes_read != bytes_desired) {

    /* aqu, ms gestin de errores ... */}...

    Con esta prctica tan aburrida los programadores tienden a ser descuidados y la incumplen siendo el resultado unprograma que no gestiona adecuadamente las excepciones. Por otro lado, si se realiza adecuadamente, los programasse vuelven ilegibles debido a que hay mucha gestin de errores que embrolla el cdigo significativo.

    En Ruby, como en muchos lenguajes modernos, se pueden gestionar las excepciones para bloques de cdigo de unaforma compartimentalizada, lo que permite tratar los imprevistos de forma efectiva sin cargar excesivamente ni alprogramador ni a cualquier otra persona que intente leer el cdigo posteriormente. El bloque de cdigo marcado conbegin se ejecutar hasta que haya una excepcin, lo que provoca que el control se transfiera a un bloque con el cdigode gestin de errores, aquel marcado con rescue. Si no hay excepciones, el cdigo de rescue no se usa. El siguientemtodo devuelve la primera lnea de un fichero de texto o nil si hay una excepcin.

    def first_line( filename )begin

    file = open(filename)info = file.getsfile.closeinfo # Lo ltimo que se evala es el valor devuelto

    rescue

    nil # No puedo leer el fichero, luego no devuelvo una cadenaend

    end

    48

  • Captulo 22. Procesamiento de excepciones: rescue

    A veces nos gustara evitar con creatividad un problema. A continuacin, si el fichero no existe, se prueba a utilizar laentrada estndar:

    beginfile = open("algun_fichero")

    rescue

    file = STDINendbegin

    # ... procesamos la entrada ...rescue

    # ... aqu tratamos cualquier otra excepcinend

    Dentro del cdigo de rescue se puede utilizar retry para intentar de nuevo el cdigo en begin. Esto nos permitereescribir el ejemplo anterior de una forma ms compacta:

    fname = "algun_fichero"begin

    file = open(fname)# ... procesamos la entrada ...

    rescue

    fname = "STDIN"retry

    end

    Sin embargo, este ejemplo tiene un punto dbil. Si el fichero no existe este reintento entrar en un bucle infinito. Esnecesario estar atento a estos escollos cuando se usa retry en el procesamiento de excepciones.

    Toda biblioteca Ruby genera una excepcin si ocurre un error y se pueden lanzar excepciones explcitamente dentrodel cdigo. Para lanzar una excepcin utilizamos raise. Tiene un argumento, la cadena que describe la excepcin. Elargumento es opcional pero no se debera omitir. Se puede acceder a l posteriormente a travs de la variable globalespecial $!.

    ruby> beginruby| raise "error"ruby| rescueruby| print "Ha ocurrido un error: ", $!, "\n"ruby| endHa ocurrido un error: errornil

    49

  • Captulo 23. Procesamiento de excepciones:ensure

    Cuando un mtodo termina su ejecucin puede que se necesiten ciertas labores de limpieza. Quizs se tenga que cerrarun fichero abierto, se deban liberar los datos de los buffers, etc. Si siempre hubiese un nico punto de salida para cadamtodo, se podra poner con seguridad todo el cdigo de limpieza en un lugar concreto y saber que se ejecutar, sinembargo, un mtodo podra retornar en varios lugares o el cdigo de limpieza podra saltarse inesperadamente debidoa una excepcin.

    beginfile = open("/tmp/algun_fichero","w")# ... Escribimos en el fichero ...file.close

    end

    En el ejemplo superior, si ocurre una excepcin durante la parte del cdigo en que se escribe en el fichero, ste quedarabierto. Si no deseamos recurrir al siguiente tipo de redundancia:

    beginfile = open("/tmp/algun_fichero","w")# ... Escribimos en el fichero ...file.close

    rescue

    file.closefail # levantamos una excepcin

    end

    Que es desgarbada y se nos escapa de las manos cuando el cdigo se vuelve ms complicado debido a que hay quetratar todo return y break.

    Por esta razn se aadi otra palabra reservada a esquema begin ... rescue ... end, ensure. El bloque de cdigo deensure se ejecuta independientemente del xito o fracaso del bloque de cdigo en begin.

    beginfile = open("/tmp/algun_fichero","w")# ... Escribimos en el fichero ...

    rescue

    # ... gestionamos las excepciones ...ensure

    file.close # ... Y esto se ejecuta siempre.end

    Se puede utilizar ensure sin rescue y viceversa, pero si se utilizan en el mismo bloque begin ... end, rescue debepreceder a ensure.

    50

  • Captulo 24. AccesoresEn un captulo anterior se trat brevemente las variables instancia, pero no se hizo mucho con ellas. Las variablesinstancia de un objeto son sus atributos, eso que diferencia a un objeto de otro dentro de la misma clase. Es importantepoder modificar y leer estos atributos; lo que supone definir mtodos denominados accesores de atributos. Veremosen un momento que no siempre hay que definir los mtodos accesores explcitamente, pero vayamos paso a paso. Losdos tipos de accesores son los de escritura y los de lectura.

    ruby> class Frutaruby| def set_kind(k) # escritorruby| @kind = kruby| endruby| def get_kind # lectorruby| @kindruby| endruby| endnilxruby> f1 = Fruta.new#ruby> f1.set_kind("melocotn") #utilizamos el escritor"melocotn"ruby> f1.get_kind #utilizamos el lector"melocotn"ruby> f1 #inspeccionamos el objeto#

    Sencillo; podemos almacenar y recuperar informacin sobre la clase de fruta que queremos tener en cuenta. Pero losnombres de nuestros mtodos son un poco largos. Los siguientes son ms breves y convencionales:

    ruby> class Frutaruby| def kind=(k)ruby| @kind = kruby| endruby| def kindruby| @kindruby| endruby| endnilruby> f2 = Fruta.new#ruby> f2.kind = "banana""banana"ruby> f2.kind"banana"

    51

  • Captulo 24. Accesores

    24.1. El mtodo inspectEn estos momentos es adecuada una pequea disgresin. Ya se habr notado que cuando deseamos ver directamente unobjeto se nos muestra algo crptico como lo siguiente #. Este es un comportamiento por defectoque se puede modificar. Todo los que se necesita es definir un mtodo denominado inspect. ste puede devolver unacadena que describa el objeto de una forma razonable, incluyendo el estado de alguna o todas las variables instancia.

    ruby> class Frutaruby| def inspectruby| "una fruta de la variedad " + @kindruby| endruby| endnilruby> f2una fruta de la variedad banana

    Un mtodo relacionado es to_s (convertir a cadena) que se utiliza al imprimir un objeto. En general se puede pensarque inspect es una herramienta para cuando se escriben y depuran programas, y to_s una forma de refinar la salidade un programa. eval.rb utiliza inspect cuando muestra resultados. Se puede utilizar el mtodo p para obtener consencillez resultados para la depuracin de programas.

    # las dos lneas siguientes son equivalentesp anObjectprint anObject.inspect, "\n"

    24.2. Facilitando la creacin de accesoresDado que muchas variables instancia necesitan mtodos accesores, Ruby proporciona abreviaturas para las formasconvencionales.

    Tabla 24-1. Accesores

    Abreviatura Efectoattr_reader :v def v; @v; endattr_writer :v def v=(value); @v=value; endattr_accesor :v attr_reader :v; attr_writer :vattr_accesor :v, :w attr_accesor :v; attr_accessor :w

    Tomemos ventaja de esto y aadamos informacin fresca. Primero pediremos la generacin de un escritor y un lectory luego incorporaremos la nueva informacin en inspect.

    ruby> class Frutaruby| attr_accessor :conditionruby| def inspect

    52

  • Captulo 24. Accesores

    ruby| "una " + @kind + " " + @conditionruby| endruby| endnilruby> f2.condition = "madura""madura"ruby> f2una banana madura

    24.3. Ms diversin con la frutaSi nadie se come nuestra fruta madura, quizs es momento de que pague su precio.

    ruby> class Frutaruby| def time_passesruby| @condition = "podrida"ruby| endruby| endnilruby> f2una banana maduraruby> f2.time_passes"podrida"ruby> f2una banana podrida

    Pero mientras estbamos jugando con esto se ha introducido un pequeo problema. Qu ocurre si intentamos crearuna tercera pieza de fruta en estos momentos? Recurdese que las variables instancia no existen hasta que no se lesasigne valor.

    ruby> f3 = Fruta.newERR: failed to convert nil into String

    El que se queja es el mtodo inspect y con motivos. Se le ha indicado que informe sobre el tipo y la condicin deuna pieza de fruta, pero f3 no tiene asignado ninguno de sus atributos. Si se quiere, es posible redefinir este mtodopara que compruebe que las variables instancia estn definidas (utilizando el mtodo defined?) e informar de ellosslo si es as aunque esto puede que no sea de mucha utilidad dado que toda pieza de fruta es de un tipo y est en unadeterminada condicin, parece que se debiera asegurar que los atributos se definen de alguna forma. Este es el temadel siguiente captulo.

    53

  • Captulo 25. Inicializacin de objetosLa clase Fruta del captulo anterior tiene dos variables instancia, una para describir la clase de fruta y otra paradescribir su estado. Despus de redefinir el mtodo inspect de la clase, nos dimos cuenta de que no tiene sentido queuna pieza de fruta carezca de esas caractersticas. Afortunadamente, Ruby tiene un mecanismo para asegurar que lasvariables instancia se inicialicen siempre.

    25.1. El mtodo initializeSiempre que Ruby crea un objeto nuevo, busca un mtodo llamado initialize y lo ejecuta. Luego lo ms sencillo quese puede hacer es utilizar este mtodo para dar valores a las variables instancia, as el mtodo inspect no tiene nadapor lo que quejarse.

    ruby> class Frutaruby| def initializeruby| @kind = "manzana"ruby| @condition = "madura"ruby| endruby| endnilruby> f4 = Fruta.newuna manzana madura

    25.2. Modificando suposiciones por requisitosHay veces en las que no tiene mucho sentido la presencia de valores por defecto. Existe una cosa tal como una frutapor defecto? Es preferible que se deba especificar el tipo en el momento de la creacin de cada pieza de fruta. Parahacer esto se debe aadir un argumento formal al mtodo initialize. Por razones en las que no vamos a entrar, losargumentos que se entregan a new se pasan a initialize

    ruby> class Frutaruby| def initialize(k)ruby| @kind = kruby| @condition = "madura"ruby| endruby| endnilruby> f5 = Fruta.new "pera"una pera maduraruby> f6 = Fruta.newERR: (eval):1:in initialize: wrong # of arguments(0 for 1)

    54

  • Captulo 25. Inicializacin de objetos

    25.3. Inicializacin flexibleHemos visto que una vez que se asocia un argumento al mtodo initialize no se puede omitir sin que se genere unerror. Si queremos ser ms considerados podemos utilizar el argumento si se proporciona, y en caso contrario, recurriral valor por defecto.

    ruby> class Frutaruby| def initialize(k="manzana")ruby| @kind = kruby| @condition = "madura"ruby| endruby| endnilruby> f5 = Fruta.new "pera"una pera maduraruby> f6 = Fruta.newuna manzana madura

    Se pueden utilizar los valores por defecto de un argumento en cualquier mtodo no slo en initialize. Los argumentoshay que organizarlos de tal forma que aquellos con valores por defecto aparezcan al final de la lista.

    A veces es til tener varias formas de inicializar un objeto. Aunque est fuera del mbito de este tutorial, Ruby permitereflexin sobre los objetos y listas de argumentos de tamao variable. Ambas tcnicas combinadas permiten realizarsobrecarga de mtodos.

    55

  • Captulo 26. EntresijosEste captulo trata algunos problemas prcticos.

    26.1. Delimitadores de sentenciasAlgunos lenguajes requieren algn tipo de puntuacin, a menudo el punto y coma (;), para finalizar toda sentencia deun programa. Por el contrario, Ruby recurre al convenio seguido por shells como sh y csh. Varias sentencias en unalnea se han de separar con puntos y comas sin que se necesite al final de la lnea; LF se trata como un punto y coma.Si una lnea termina en \ (backslash) se ignora el LF que le sigue; lo que permite tener una nica lnea lgica quecomprende varias lneas fsicas.

    26.2. ComentariosPor qu escribir comentarios? Aunque el buen cdigo tiende a ser auto-descriptivo, a menudo es til realizar comen-tarios en el margen. Es un error creer que otras personas que examinen el cdigo comprendan inmediatamente lo quese pretenda hacer. Aparte, y desde una perspectiva prctica, quin de nosotros no ha tenido que realizar una correc-cin o mejora en un programa despus de un cierto periodo de tiempo y decir: he escrito esto, pero qu demonios sesupone que hace?

    Algunos programadores experimentados sealarn, con bastante razn, que comentarios contradictorios o desactual-izados pueden ser peor que ningn comentario en absoluto. Evidentemente, los comentarios no deben ser un sustitutode un cdigo legible; si el cdigo es poco claro, es probable que tambin sea errneo. Es probable que se necesitecomentar ms cuando se est aprendiendo Ruby y menos cuando se llegue a expresar las ideas en cdigo sencillo,elegante y legible.

    Ruby sigue el convenio, comn entre los lenguajes de guiones, de utilizar el smbolo de la almohadilla para indicar elcomienzo de un comentario. El interprete ignora cualquier cosa que siga a una almohadilla, que no est entre comillas,hasta el final de la lnea en la que aparece

    Para facilitar la escritura de grandes bloques de comentarios el interprete tambin ignora cualquier cosa comprendidaentre una lnea inicial con =begin y una final con =end.

    #!/usr/bin/ruby

    =begin***********************************************************************

    Este es un bloque de comentarios, algo que se escribe en beneficio delos lectores (incluido uno mismo). El interprete lo ignora. No haynecesidad de utilizar # al comienzo de cada lnea***********************************************************************

    =end

    56

  • Captulo 26. Entresijos

    26.3. Organizacin del cdigoEl intrprete de Ruby procesa el cdigo conforme lo lee. No existe nada semejante a una fase de compilacin; si algono se ha ledo tod