Librerías predefinidas

P: ¿Qué devuelve instance_methods(nil)?
P: ¿Cómo funcionan las semillas de los números aleatórios?
P: ¿Cuál es la diferencia entre un valor inmediato y una referencia?
P: ¿Cuál es la diferencia entre nil y false?
P: Se ha leído un fichero y modificado, pero no se han reflejado en el disco los cambios.
P: ¿Cómo puedo procesar un fichero y actualizar sus contenidos?
P: ¿He creado un fichero, lo he copiado y al final parece que la copia se ha perdido?
P: ¿Cómo puedo obtener el número de línea en un fichero de entrada?
P: ¿Cómo puedo utilizar less para mostrar la salida de un programa?
P: ¿Qué le ocurre a un objeto File cuando ya no se le referencia?
P: No me siento a gusto si no cierro el fichero
P: ¿Cómo puedo ordenar fichero por fecha de modificación?
P: ¿Cómo puedo obtener la frecuencia de las palabras en fun fichero?
P: ¿Por qué no es false una cadena vacía?
P: ¿Cómo puedo ordenar cadenas alfabéticamente?
P: ¿Qué devuelve "abcd"[0]?
P: ¿Cómo puedo expandir los tabuladores a espacios?
P: ¿Cómo puedo representar una barra invertida en una expresión regular?
P: ¿Cuál es la diferencia entre sub y sub!?
P: ¿Con qué coincide \Z?
P: ¿Cuál es la diferencia entre ".." y "..."?
P: ¿Tiene Ruby punteros a funciones?
P: ¿Cuál es la diferencia entre thread y fork?
P: ¿Cómo puedo utilizar Marshal?
P: ¿Posee Ruby manejo de excepciones?
P: ¿Cómo puedo utilizar trap?

P: ¿Qué devuelve instance_methods(nil)?

R: El método instance_methods devuelve un array que contiene los nombres de los métodos a los que responde el receptor. Incluye los métodos de las superclases y de los módulos mezclados.

instance_methods(nil) devuelve los métodos definidos en la clase del objeto.

P: ¿Cómo funcionan las semillas de los números aleatórios?

R: Depende. Antes de la versión 1.5.2 de Ruby, el generador de números aleatórios tenía (por defecto) una semilla constante y por lo tanto producía la misma serie de números cada vez que se ejecutaba un programa. Si se necesitan comportamientos menos deterministas, hay que llamar a srand para establecer una semilla menos predecible.

Versiones más modernas tienen un comportamiento diferente. Si se llama a rand sin una llamada previa a srand, Ruby genera su propia semilla aleatória. Ejecuciones posteriores de un programa que no utiliza srand generarán secuencias diferentes de números aleatorios. Para obtener el antiguo y predecible comportamiento, hay que llamas a srand con una semilla constante.

P: ¿Cuál es la diferencia entre un valor inmediato y una referencia?

R: Fixnum, true, nil y false esrán implementados como valores inmediatos. En los valores inmediatos, las variables contienen los objetos mismos no referencias a ellos.

Los métodos singleton no se pueden definir como objetos de este tipo. Dos Fixnum con el mismo valor siempre representan la misma instancia luego (por ejemplo) variables de instancia con el valor de "uno" están compartidas entre todas las instancia "uno" del sistema. Esto hace imposible definir un método singleton para una de ellas.

P: ¿Cuál es la diferencia entre nil y false?

R: Primero las igualdades. nil y false son los dos únicos valores que se evalúan a falso en un contexto booleano.

Sin embargo, se tratan de instancias de clases diferentes (NilClass y FalseClass), y en cualquier otro lugar tienen comportamientos diferentes.

Se recomienda que los métodos de predicado (aquellos que terminan en una interrogación) devuelvan true o false. Aquellos que deban indicar un fallo deben devolver nil.

P: Se ha leído un fichero y modificado, pero no se han reflejado en el disco los cambios.

R:

	   
	   open("example","r+").readline.each_with_index{|l, i|
	     l[0,0] = (i + 1).to_s + ": " }
	 
Este programa no añade los números de línea al fichero "example". Lee el fichero y para cada línea leída añade un numero de línea, pero nunca actualiza la línea. El siguiente extracto sí que actualiza el fichero (aunque de una manera un tanto peligrosa pues no realiza una copia de seguridad antes de la actualización).
	   
	   io = open("example","r+")
	   ary = io.readlines
	   ary.each_with_index([l, i| = (i + 1).to_s + ": "}
	   io.rewind
	   io.print ary
	   io.close
	 

P: ¿Cómo puedo procesar un fichero y actualizar sus contenidos?

R: Utilizando la opción de línea de comandos -i o con la variable predefinida $-i, se puede leer un fichero y reemplazarlo.

El código del punto anterior, que añade los números de línea a un fichero, sería probablemente mejor utilizando esta técnica.

	   
	   $ ruby -i -ne 'print "#$.: #$_"' example
	 
Si se desea preservar el fichero original hay que utilizar -i.bak para crear una copia de seguridad.

P: ¿He creado un fichero, lo he copiado y al final parece que la copia se ha perdido?

R: Esta pieza de código no funciona correctamente:

	   
	   open('file','w').print "Este es un fichero.\n"
	   system 'cp file newfile'
	 
debido a que la E/S es con buffers, file se copia antes de que su contenido se haya grabado en el disco. newfile probablemente esté vacío. Sin embargo, cuando termina el programa, todos los buffers se liberan y file tiene el contenido esperado.

Este problema no aparece si se cierra el fichero antes de realizar la copia.

	   
	   f = open('file','w')
	   f.print "Este es un fichero.\n"
	   f.close
	   system 'cp file newfile'
	 

P: ¿Cómo puedo obtener el número de línea en un fichero de entrada?

R: A medida que se lee un fichero, Ruby incrementa el contador de número de líneas de la variable global $.. Disponible también a través del atributo lineno de un objeto File.

La variable constante ARGF es un objeto de tipo fichero que se puede utilizar para leer todos los ficheros de entrada indicados en la líena de comandos (o de la entrada estándar si no hay ficheros). ARGF se utiliza implicitamente en código como el siguiente:

	    
	    while gets
	      print $_
	    end
	  
En este caso, $. acumulará el número de líneas leídas de todos los ficheros. Para obtener el número de línea del fichero en curso, hay que utilizar
	    
	    ARGF.file.lineno
	  
También se puede utilizar el nombre del fichero en curso con ARGF.file.path.

P: ¿Cómo puedo utilizar less para mostrar la salida de un programa?

R: Si se intenta los siguiente, no funciona.

	   
	   f = open '|less', 'w'
	   f.print "abc\n"
	 
Esto es debido a que el program termina inmediatamente, y less nunca tiene la oportunidad de ver el contenido de lo que se escribe, y por lo tanto no lo muestra. Hay que utilizar close para esperar a que less termine. f = open '|less', 'w' f.print "abc\n" f.close

P: ¿Qué le ocurre a un objeto File cuando ya no se le referencia?

R: Un objeto File que ya no está referenciado pasa a estar a disposición del recolector de basura. El fichero se cerrará automáticamente cuando es recogido como basura por el recolector.

P: No me siento a gusto si no cierro el fichero

R: Existen al menos cuatro buenas maneras de asegurarse del cierre de un fichero:

	  (1) f = open "fichero"
	      begin
	        f.each {|l| print l}
	      ensure
	        f.close
	      end
	      
	   (2) File.open("fichero") { |f|
	         f.readlines.each{ |l| print l }
	       }

	   (3) IO.foreach("fichero" { |l| print l }

	   (4) IO.readlines("fichero").each {|l| print l }
	

P: ¿Cómo puedo ordenar fichero por fecha de modificación?

R:

	  
	  Dit.glob("*").sort{|a, b| File.mtime(b) <=> File.mtime(a) }
	
Aunque funciona (devuelve una lista clasificada en orden cronológico inverso) es muy ineficiente, ya que recupera la hora de modificación del fichero del sistema operativo para cada comparación.

Una forma mucho más eficiente se puede obtener añadiendo un poco de complejidad:

	  
	  Dir.glob("*").collect {|f| [File.mtime(f),f]}.
	    sort{|a, b| b{0} <=> a[0].collect!{|e| e[1]}
	

P: ¿Cómo puedo obtener la frecuencia de las palabras en fun fichero?

R:

	  
	  freq = Hash.new(0)
	  open("example").read.scan(/\w+/){|w| freq[w] += 1 }
	  freq.keys.sort.each {|k| print k, "-", freq[k], "\n"}
	
Produce:
	  
	  and-1
	  is-3
	  line-3
	  one-1
	  this-3
	  three-1
	  two-1
	

P: ¿Por qué no es false una cadena vacía?

R: ¡En Perl, una cadena vacía ("") devuelve false y Ruby true en una expresión condicional!

En Ruby, y dentro de contextos condicionales sólo es falso nil y false. Es una manera de obtener velocidad - tanto nil como false son valores inmediatos por lo que se pueden chequear sin tener que seguir la referencia al objeto.

Para comprobar si está vacía una cadena se puede utilizar empty?, o comparar directamente con "" o verificar si la longitud (length) es 0.

P: ¿Cómo puedo ordenar cadenas alfabéticamente?

R: Si se desea ordenar cadenas como 'AAA', 'BBB', ... 'ZZZ', 'aaa', 'bbb', ... los operadores incluídos en el lengueje valdrán. Pero si se desea ordenar distinguiendo entre mayúsculas y ninúsculas, hay que comparar dentro del bloque de ordenación la versión en minúsculas:

	  
	  array = %w(z bB Bb BB bb Aa aA AA aa a)
	  puts array.sort{ |a, b| a.downcase <=> b.downcase }
	
Produce:
	  
	  a
	  aa
	  AA
	  aA
	  Aa
	  bb
	  BB
	  bB
	  Bb
	  z
	

Si se desea ordenar de tal forma que las Aes mayúsculas y las aes munúsculas aparezcan juntas, pero se considera que a es mayor que A (es decir que 'Aa' aparece después de AA pero antes de AB), hay que utilizar:

	  
	  puts array.sort { |a, b|
	    (a.downcase <=>b.downcase).nonzero?  || a <=> }
	
Produce:
	  
	  a
	  AA
	  Aa
	  aA
	  aa
	  BB
	  Bb
	  bB
	  bb
	  z
	

P: ¿Qué devuelve "abcd"[0]?

R: Devuelve el código del caracter 'a', 97 (Fixnum). Se puede expresar el código de un caracter como entero anteponiendo al mismo una interrogación, luego ?a es también 97 (Fixnum).

P: ¿Cómo puedo expandir los tabuladores a espacios?

R: Si a contiene la cadena a expandir, se podría utilizar uno de los siguientes códigos:

         
	 1 while a.sub!(/(^[^\t]*)\t(\t*)/){$1+' '+*(8-$1.size%8*$2.size)}
       

         1 while a.sub!(/\t(\t*)/){' '*(8-$ .begin(0)%8+8*$1.size)}
       
         
	 a.gsub!(/([^\t]{8})|([^\t]*)\t/n){[$+].pack("A8")}         
       

P: ¿Cómo puedo representar una barra invertida en una expresión regular?

R: Regex.quote('\\') representa a una barra invertida.

Es más problemático si se utiliza sub y gsub, digamos que se tiene gsub(/\\/,'\\\\') con lo que se espera reemplazar toda barra invertida por dos. Durante el análisis sintáctico el segundo parámetro pasa a ser '\\'. Cuando tiene lugar la sustitución, el motor de expresiones regulares lo pas a '\', luego el efecto que se obtiene es reemplazar toda barra invertida por otra. Hay que escribir gsub!(/\\/,'//////')!.

Pero, dado que \& contiene la cadena a buscar, se posría escribir gsub(/\\/,'/&/&').

Si se utilizan los bloques de gsub, p. e. gsub(/\\/){'\\\\'}, la cadena de sustitución sólo se analida una vez (surante la fase sintáctica) y el resultado es el deseado.

P: ¿Cuál es la diferencia entre sub y sub!?

R: Con sub se genera una copia del receptor, se sustituye y se devuelve.

Con sub!, se modifica el receptor y se devuelve su se encuentra una coincidencia. Sino se devuelve nil.

Los métodos como sub! se conocen como métodos destructivos (ver la sección 7.13) es decir, modifican los atributos del receptor. Si existen dos métodos similares y uno de ellos es destructivo, el destructivo lleva como sufijo !.

	  
	  def foo(str)
	    str = str.sub(/foo/,"baz")
	  end

	  obj = "foo"
	  foo(obj)                   # -> "baz"
	  obj                        # -> "foo"

	  def foo(str)
	    str = str.sub!(/foo/,"baz")
	  end

	  foo(obj)                   # -> "baz"
	  obj                        # -> "baz"
	

P: ¿Con qué coincide \Z?

R: \Z coincide con el carácter anterior a un \n si la cadena finaliza con él, sino con el final de la cadena.

P: ¿Cuál es la diferencia entre ".." y "..."?

R: .. incluye el extremo derecho del rango, ... no.

P: ¿Tiene Ruby punteros a funciones?

R: Desde una variable se puede referenciar a un objeto Proc obtenido con Proc.new.proc o lambda, por lo que se puede considerar que esa variable es un puntero a una función. Se pueden obtener referencias a métodos dentro de una instancia concreta de un objeto utilizando Object.method.

P: ¿Cuál es la diferencia entre thread y fork?

R: Los threads en Ruby están implementados en el interprete, mientras que fork realiza una llamada al sistema operativo para crear un proceso en ejecución independiente.

Thread y fork tienen las siguientes características:

  • fork es lento, thread no.

  • fork no comparte el espacio de memoria.

  • thread no provoca trasiego.

  • thread funciona sobre DOS.

  • Cuando un thread se bloquea, todo el proceso se detiene.

  • fork puede aprovechar las paradas en espera de E/S, thread no (al menos sin algún tipo de ayuda).

Probablemente la mejor opción es una mezcla de fork y thread.

P: ¿Cómo puedo utilizar Marshal?

R: Marshal se utiliza para almacenar un objeto en un fichero o en una cadena y recuperarlo posteriormente. Los objetos se pueden almacenar utilizando:

	  
	  Marshal.dump obj [, io][, lev]
	
io es un objeto de E/S preparado para escritura, lev designa el nivel al cual los objetos se derreferenciarán y se almacenarán. Si los niveles lev ya está derreferenciados y las referencias aún existen entonces dump sólo almacena la referencia y no el objeto referenciado. Esto no es bueno ya que los objetos referenciado no pueden reconstruirse posteriormente.

Si se omite io, los objetos aplanados se devuelven en una cadena.

Se pueden recuperar los objetos con

	  
	  obj = Marshal.load io
	  # o
	  obj = Marshal.load str
	
Donde io es un objeto de E/S de lectura y str es una cadena.

P: ¿Posee Ruby manejo de excepciones?

R: Ruby tiene un esquema flexible de manejo de excepciones:

	  
	  begin
	    sentencias que pueden generar una excepción
	  rescue [nombres de clases de excepción]
	    sentencias a ejecutar cuando ha ocurrido una excepción
	  rescue [nombres de clases de excepción]
	    sentencias a jecuy¡tar cuando ha ocurrido una excepción
	  ensure
	    sentencias que siempre se han de ejecutar.
	  end
	
Si ocurre una excepción en la clausula begin, se ejecutarán las sentencias de la clausula rescue que tenga un nombre de excepción que coincida con la generada. La clausula ensure se ejecuta haya o no excepción. las clausulas rescue y ensure pueden omitirse.

Si en la clausula rescue no se indica ninguna clase de excepción, se supone que es StandardError y toda escepción descendiente de ésta es recogida.

Esta expresión devuelve el valor de la clausula begin.

Con $! se puede acceder a la última excepción generada (y su tipo se puede determinar con $!.type).

P: ¿Cómo puedo utilizar trap?

R: trap asocia bloques de código con sucesos externos (señales).

	  
	  trap("PIPE") { raise "SIGPIPE" }