Subsecciones

Sugerencias

  1. Repositorio privado en GitHub

Lectura de las matrices

Supongamos este fichero de entrada:

[~/local/src/ruby/LPP/matrix(master)]$ cat datos.dat
1 2 3
4 5 6

1 2
3 4
5 6
En una primera aproximación podríamos leer el fichero siguiendo una secuencia de comandos como esta:
[~/local/src/ruby/LPP/matrix(master)]$ pry
[1] pry(main)> data = File.open('datos.dat').read
=> "1 2 3\n4 5 6\n\n1 2\n3 4\n5 6\n"
[2] pry(main)> puts data
1 2 3
4 5 6

1 2
3 4
5 6
=> nil
La clase File provee los métodos para trabajar con ficheros. El método read esta definido en la clase IO de la cual File hereda.
[5] pry(main)> File.ancestors.include? IO
=> true
Pero claro, tenemos que a partir de la cadena data obtener las dos matrices. Podemos usar split para obtener las dos cadenas que contienen las respectivas matrices:
[4] pry(main)> a, b = data.split(/\n\n+/)
=> ["1 2 3\n4 5 6", "1 2\n3 4\n5 6\n"]
[5] pry(main)> puts a
1 2 3
4 5 6
=> nil
[6] pry(main)> puts b
1 2
3 4
5 6
=> nil
Pero aun queda por resolver el problema de pasar de la cadena a la matriz.

Para hacerlo, primero escribimos una función mapmap que es una extensión de map para matrices:

def mapmap(a)
  a.map { |r| 
    r.map { |e| 
      yield e
    }
  }
end
Podemos usar mapmap asi:
[~/local/src/ruby/LPP/matrix(master)]$ pry
[1] pry(main)> require './matrix'
=> true
[2] pry(main)> a = [[1,2],[3,4]]
=> [[1, 2], [3, 4]]
[3] pry(main)> mapmap(a) { |x| x*x }
=> [[1, 4], [9, 16]]
[4] pry(main)> mapmap(a) { |x| x-1 }
=> [[0, 1], [2, 3]]
[5] pry(main)>

Ahora podemos escribir un método to_m que convierte la cadena con los datos en una matriz:

def to_m(a)
  a = a.split(/\n/)
  a = a.map { |r| r.split(/\s+/) }
  a = mapmap(a) { |x| x.to_f } 
end

Usando to_m podemos construir las matrices a partir de los datos de entrada:

[~/local/src/ruby/LPP/matrix(master)]$ pry
[1] pry(main)> require './matrix'
=> true
[2] pry(main)> data = File.open('datos.dat').read 
=> "1 2 3\n4 5 6\n\n1 2\n3 4\n5 6\n"
[3] pry(main)> a, b = data.split(/\n\n+/)
=> ["1 2 3\n4 5 6", "1 2\n3 4\n5 6\n"]
[4] pry(main)> a = to_m(a)
=> [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
[5] pry(main)> a[0][0].class
=> Float
Combinando todo lo visto, podemos escribir el método read_matrices(fn) que lee el fichero de datos y retorna las dos matrices:
def read_matrices(fn)
  text = File.open(fn).read

  a, b = text.split(/\n\n+/)
  a = to_m(a)
  b = to_m(b)

  [a, b]
end

Producto

Podemos ahora leer las matrices con read_matrices:

[~/local/src/ruby/LPP/matrix(master)]$ pry
[1] pry(main)> require './matrix'
=> true
[2] pry(main)> a, b = read_matrices('datos.dat')
=> [[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]]
[11] pry(main)> a
=> [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
[12] pry(main)> b
=> [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]
Podemos detectar el número de filas y columnas de la matriz así:
[3] pry(main)> a.length
=> 2
Supuesto que todas las filas tienen la misma longitud, el número de columnas viene dado por la longitud de cualquier fila:
[4] pry(main)> a[0].length
=> 3

Para multiplicar los elementos de la primera fila de a con los elementos de la primera columna de b podemos hacer:

[6] pry(main)> z = a[0].map.with_index { |x, i| x*b[i][0] }
=> [1.0, 6.0, 15.0]
El método transpose de la clase Array da la traspuesta de una matrix:
[6] pry(main)> bt = b.transpose
=> [[1.0, 3.0, 5.0], [2.0, 4.0, 6.0]]
Para multiplicar los elementos de la primera fila de a con los elementos de la primera columna de b podemos también hacer:
[8] pry(main)> z = a[0].map.with_index { |x, i| x*bt[0][i] }
=> [1.0, 6.0, 15.0]
Para multiplicar los elementos de la primera fila de a con los elementos de la segunda columna de b podemos hacer:
[9] pry(main)> z = a[0].map.with_index { |x, i| x*bt[1][i] }
=> [2.0, 8.0, 18.0]
Podemos usar el método reduce (tambien conocido como inject ) para sumar los elementos de z:
[10] pry(main)> c01 = z.reduce(0) { |s, x| s+x }
=> 28.0

Por supuesto también podemos usar bucles sobre rangos como se ilustra en este ejemplo en el que multiplicamos la 2ª fila por la 2ª columna:

[10] pry(main)> a
=> [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
[11] pry(main)> b
=> [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]
[12] pry(main)> s = 0
=> 0
[17] pry(main)> for k in (0...3) do s += a[1][k]*b[k][1]  end
=> 0...3
[18] pry(main)> s
=> 64.0

Casiano Rodriguez León 2015-01-07