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 6En 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 => nilLa 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 => truePero 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 => nilPero 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 } } endPodemos 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 => FloatCombinando 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
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 => 2Supuesto 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