"La Gaceta de Linux...¡haciendo Linux un poco más divertido!"


Programando en Ruby, parte 1

por Hiran Ramankutty

Traducción al español por Daniel Guerrero
el día 22 de Febrero de 2003, para La Gaceta de Linux


Introducción

Ruby es un lenguaje interpretado, Orientado a Objetos puros diseñado por Yukihiro Matsumoto de Japón, donde se reporta que ¡es más popoular que Python y Perl! La primera parte de estas serie será un tutorial introductorio, con cosas más avanzadas en desarrollo.

Claro, no necesito hacer el ritual de defender las 'ventajas de Ruby comparado con los lenguajes X, Y y Z' - la mayoría de la gente se da cuenta que cada lenguaje tiene un sabor único y un caracter propio - si escoges Python o Ruby para tu proyecto de Software Libre depende más de una afinidad particular que sientas individualmente por algún lenguaje que otro, y la disponibilidad de las facilidades de las librerías estándas, más que en arcanos apartados técnicos. ¡Así que disfruta el sabor único de Ruby!

Requisitos

Asumo que tu medio de desarrollo es Linux y que tienes Ruby instalado. Ruby es software libre, y no existen restricciones en su uso. Puedes obtenerlo de la página principal de Ruby .

Hola Mundo

Iniciaremos con el obligado 'Hello, World'.


% cat > hello.rb
print "Hola Mundo\n"
^D
% ruby hello.rb
Hola Mundo
%

Variables

La categorización es hecha basado en el primer caracter del nombre del identificador:


                $                       variable global
                @                       variable de instancia
                a-z or '_'              variable local
                A-Z                     constante

Las dos 'pseudo-variables' son excepciones a la regla establecida arriba. Estas son 'self' y 'nil'

Ambas son llamadas como si fueran variables locales, ¡pero no lo son! veremos su significado real después.

Variables Globales

Una variable global tiene su nombre iniciando con una $. Por tanto, puede ser referido en cualquier lugar del programa. Se debe notar que una variable global asume el valor 'nil' antes de inicializarla. Puedes probar esto con:


 % ruby
 print $foo,"\n"
 $foo = 5
 print $foo,"\n"
 ^D
 %

El intérprete responde con:

nil
5

Es posible para nosotros 'ligar' procedimientos a variables globales, los procedimientos son automáticamente invocados cuando las variables son cambiadas. ¡Más de esto después!

Algunos tipos especiales de variables globales formados con un sólo caracter que sigue al signo '$' son, como una colección, interpretados por Ruby como un sistema más amplio de variables (de sólo lectura). Algunos de ellos son dados abajo tanto con su significado.

Variables locales

Una variable local tiene su nombre inciiando con una letra minúscula o un '_'. A diferencia de las variables globales y las de instancia, estas no asumen el valor 'nul', pero tienen el comportamiento siguiente:


% ruby
print foo
^D

Obtendrás este mensaje de error
      "undefined local variable or method 'foo' for #(object...)".

El alcance de una variable local es confinado a uno de estos:

Si inicializamos una variable local en cualquier bloque (o un procedimiento), entonces permanece indefinido después de que salga de un ciclo. Por ejemplo

def foo(n)
	k = 2 * n
	print "\n",k,"\n"
	print defined? k,"\n"
end

foo 3
print defined? k,"\n"
^D

La salida es:
  

6
local-variable
nil

En el ejemplo de arriba 'defined?' es un operador que checa si un argumento está definido o no. El resultado "local-variable" y "nil" (para indicar falso) debe hacerlo claro.

Constantes

Cualquier nombre con caracteres siguiendo a una letra mayúscula es tratada como una constante. Pero los programadores de Ruby, para evitar confusiones usan nombres con todas las letras mayúsculas. Así que 'Foo' como también 'FOO' son constantes. Como en el caso de una variable local, una constante es definida por una sustitución y un acceso a una constante indefinida o la alteración de una constante definida causará un error. Verifícalo por ti mismo.

Cadenas

Las cadenas en ruby pueden ser con una comilla ('...') o con comillas dobles (".."). Pero ambas son diferentes. Usa las comillas dobles si la cadena contiene caracteres de escape. También los resultados de una evaluación son incluidos en expresiones contenidas en comillas por #{}. Observa los ejemplos:

print "\n"
print '\n'
print "\001","\n"
print '\001',"\n"
print "abcd #{5*3} efg","\n"
var = " abc "
print "1234#{var}567","\n"
^D

\n
\001
abcd 15 efg
1234abc567

Aprenderemos más sobre cadenas en las siguiente sección, arreglos. Esto es para incluir las características que son similares y las manejan tanto arreglos como cadenas.

Arreglos

Los arreglos pueden ser declarados usando '[]'. Una de las características de Ruby es que los arreglos son heterogéneos.


a = [1,2,"3"]
print a,"\n"
^D

Ahora, si escribes un programa de Ruby para sumar todos los elementos del arreglo mostrados en el programa de arriba, obtendrás un error:

         Error!!String cannot be coerced into Fixnum
El '3' en el arreglo es guardado como una cadena. Ahora, si se hace algo como esto:

a = [1,2,"3"]
b = a[0] + a[1] + a[2].to_i
print b,"\n"
^D

El programa se ejecutará sin errores. El agregado a a[2] i.e. '.to_i' es la conversión del contenido de a[2] a un entero. También puedes probar '.to_s'.

Operaciones como concatenación y repetición pueden ser hechas en arreglos.


a = [1,2,"3"]
print a + ["foo","bar"]
print a * 2
^D

Obtenemos:

123foobar
123123

Es posible rebanar arreglos. Aquí hay algunos ejemplos:


a = [1,2,"3","foo","bar"]
print a[0],"\n"
print a[0,2],"\n"
print a[0..3],"\n"
print a[-2..2],"\n"
print a[-3..-1],"\n"

Arreglos y cadenas son inter-convertibles. Un arreglo puede ser convertido a una cadena con 'join', y una cadena ser separada en un arreglo con 'split'.


a = [1,2,3]
print a[2],"\n"
a = a.join(":")
print a[2],"\n"
print a,"\n"
a = a.split(":")
print a[2],"\n"
print a,"\n"
^D

El Arreglo Asociativo es otra estructura importante de datos - también es llamada un 'hash' o un 'diccionario'. Es básicamente un mapeo de nombre-valor, como se muestra abajo:


h = {1 => 2, "2" => "4"}
print hash,"\n"
print hash[1],"\n"
print hash["2"],"\n"
print hash[5],"\n"
^D

¡Espero que estos resultados sean convincentes!

Estructuras de Control

If - else

Vamos a escribir la función factorial. La definición matemática es:


      n! = 1			(cuando n==0)
      n! = n * (n-1)!		(de otra manera)
      
En ruby esto puede ser escrito como:

def fact(n)
	if n == 0
		1
	else
		n * fact(n-1)
	end
end
print fact 4,"\n"

You get 24.

Ruby ha sido llamado 'tipo Algol' debido a la repetida ocurrencia de 'end'. En esta llamada recursiva, puedes notar la ausencia de la sentencia de return (retorno). De hecho, el uso de return es permitido pero innecesario debido a que la función de ruby regresa la última expresión evaluada (¿no te suena esto un poco como Lisp? Si insistes, ¡claro que puedes hacer Lisp en Ruby!)

El ciclo for


for i in 0..4
	cuerpo-del-for
end

Aquí i es la variable y 0..4 es el rango. En el caso de cadenas, puedes también escribir:


for i in "abc"

El ciclo while

Intenta esto

i=0
while i < 10
	print i+=1,"\n"
end

Case

Usamos la sentencia case para probar una secuencia de condiciones. Intenta esto.

i = 7
case i
when 1,2..5
	print "i en 2 a 5\n"
when 6..10
	print "i en 6 a 10\n"
end
^D

Obtienes

i en 6 a 10

2..5 significa el rango incluyendo al 2 y al 5. Verifica si i se encuentra dentro del rango.

Esto puede ser aplicado a cadenas como se muestra abajo.

case 'abcdef'
when 'aaa','bbb'
	print 'contiene aaa o bbb \n"
when /def/
	print "contiene def \n"
end
^D
contiene def

Date cuenta de la diagonal usada con "def". Es usada para denotar una expresión regular. Veremos esto más adelante.

Modificaciones con estructuras de control

La sentencia case mencionada arriba actualmente verifica en el rango (i in 2..5) como

(2..5) === i 

El operación de relación '===' es usado por case para verificar por varias condiciones a la vez. '===' se interpreta como adecuado por ruby para el objeto que aparece en la condición when.

Por consiguiente en el ejemplo las cadenas iguales son probadas con el primer when y una expresión concuerda con el segundo when.

Ahora intentaremos usar el operador '===' con la estructura if (intenta implementar funciones como isalnum(), isalpha(), isnum(), etc.)

Tu código puede ser minimizado cuando tenemos que usar construcciones if y while para sentencias individuales: como mostramos abajo

i = 7
print "contenido en 5..10\n" if (5..10) === i
print i-=1,"\n" while i > 0
^D
contenido en 5..10
6
5
4
3
2
1
0

Puedes a veces quere negar las condiciones de prueba. Un unless es un if negado, y un until es un while negado. Esto se te deja para que experimentes.

Hay cuatro maneras de interrumpir las sentencias de procesamiento de un ciclo desde adentro. Primero, como en C, break significa, escapar del ciclo completo. Segundo, next salta al inicio de la siguiente iteración del ciclo (corresponde a la sentencia continue en C). Tercer ruby tiene redo, el cual reinicia la iteración actual. El siguiente es el código en C que ilustra los significados de break, next, y redo:

while(condicion) {
  label_redo:
	goto label_next;		/* "next" de ruby */
	goto label_break;		/* "break" de ruby */
	goto label_redo;		/* "redo" de ruby */
	...
	...
  label_next:
}
label_break:
...

La sentencia return es de hecho la cuarta manera para salir de un ciclo desde adentro. De hecho return ocasiona la salida no sólo del ciclo sino también del método que contiene el ciclo.

Conclusión

Hemos examinado algunas características elementales del lenguaje - suficiente para que empieces con un poco más de codificación 'rápida-y-sucia'. Así como aprenda más sobre esta 'gema' de lenguaje, estaré compartiendo contigo mis experiencias a través de artículos futuros. ¡Adios!

Hiran Ramankutty

Soy un estudiante de último año de Ciencias Computaciones al Colegio de Ingeniería de Gobierno, Trichur. Aparte de Linux, Disfruto aprendiendo Física.


Copyright © 2002, Hiran Ramankutty.
Licencia de Copiado http://www.linuxgazette.com/copying.html
Publicado en la Edición 81 de La Gaceta de Linux, Agosto 2002