Matrix 1. Este guião não é sobre o filme Matrix, um filme de culto para nós programadores, mas sim sobre matrizes, que é o que em programação nós chamamos aos arrays de arrays. 2. Na verdade, o assunto não é completamente novo: in illo tempore estudámos os arrays de cadeias. Ora, cadeias são arrays de carateres. Logo, arrays de cadeias são matrizes de carateres. 3. Agora estamos interessados em arrays de arrays de números, com a particularidade de todos os arrays do array de arrays terem o mesmo tamanho. 4. Em C, uma matriz—array de arrays—declara-­‐se nas calmas: int m[20][50]; 5. Esta matriz m é um array de 20 arrays de números inteiros int, cada array tendo 50 elementos. A matriz contém ao todo 20*50 = 1000 elementos e ocupa 1000*4 bytes. Estes 4000 bytes estão contíguos na memória, como se fossem um array único de 1000 elementos, cada elemento representando um número inteiro int. Quando escrevemos, por exemplo m[6][15] estamos a referir-­‐nos ao 16º elemento do 7º array, elemento esse que é o 156º elemento dos tais 1000. 6. Dizemos que a matriz m tem 20 linhas (em inglês rows) e 50 colunas (em inglês columns). Na memória, os elementos estão arrumados linha a linha: primeiro os da primeira linha, depois os da segunda linha, etc. Na memória, logo a seguir ao último elemento da primeira linha vem o primeiro elemento da segunda linha e analogamente nos outros casos. 7. Portanto, as matrizes são uma simples generalização dos arrays. 8. No entanto, nós vamos usar exclusivamente matrizes declaradas dinamicamente, em que as dimensões não são fixadas na declaração mas sim durante a execução. Sendo uma matriz de números inteiros um array de array de int, vamos representá-­‐la nos nossos programas por um apontador int**: int **a; 9. A operação crucial vai ser alocar memória para a matriz, dados o número de linhas e o número de colunas. Na verdade, na técnica que vamos usar sistematicamente, ao alocar uma matriz com R linhas e C colunas, alocaremos também um array com R apontadores para int, inicializado para o endereço de base de cada linha. Observe: int **ints2_new(int rows, int cols) { int **result = (int **) malloc(rows * sizeof(int *)); int *p = ints_new(rows * cols); 1 int i; for (i = 0; i < rows; i++) { result[i] = p; p += cols; } return result; } 10. Esta função ints2_new é parecida com a função ints_new, mas sensivelmente mais sofisticada. Repare que o resultado é o endereço de base do array de apontadores para as linhas e não o endereço de base do array de números. Este está guardado no primeiro elemento do array de apontadores. Certo? 11. Note que como alocámos a zona de memória para a matriz por meio da função ints_new (a qual usa calloc), a matriz fica toda a zero. 12. Experimentemos isto lendo uma matriz da consola, após ter lido o número de linhas e o número de colunas, e mostrando a matriz lida no formado tabular habitual, isto é, na forma de uma tabela com esse número de linhas e esse número de colunas. 13. Para ler uma matriz apresentada linha a linha, podemos ler diretamente para o array dos números, sem mais complicações, usando a seguinte função ints2_get: void ints2_get(int **a, int rows, int cols) { ints_get_some(*a, rows * cols); } 14. Para escrever, vamos basear-­‐nos nas funções ints_putf e ints_putfln, que escrevem um array de inteiros numa linha, usando para cada inteiro o formato especificado: void ints_putf (char *fmt, int *a, int n) { int i; for (i = 0; i < n; i++) printf (fmt, a[i]); } void ints_putfln (char *fmt, int *a, int n) { ints_putf (fmt, a, n); printf ("\n"); } 15. Programe então, para começar, uma função ints2_putf que escreve uma matriz na consola, com o seguinte cabeçalho: void ints2_putf(char *fmt, int **a, int rows, int cols); Aqui fmt é a cadeia de formato usada para escrever cada número, a é a matriz, rows o número de linhas e cols o número de colunas. 16. Programe também uma função de teste que leia primeiro o número de linhas e o número de colunas de uma matriz, depois os valores de todos os elementos da 2 matriz, por meio da função ints2_get, e que escreva a matriz em formato tabular, com colunas de largura 4. Por exemplo, se o ficheiro de entrada for o seguinte: 3 4 1 2 3 4 5 6 7 8 9 10 11 12 Então o ficheiro de saída deve vir assim: 1 2 3 4 5 6 7 8 9 10 11 12 17. Submeta a sua função de teste na tarefa A. 18. Programe agora uma função de leitura alternativa ints2_get_elements, que lê os elementos posição a posição. A função tem um único argumento, que representa a matriz. Para cada elemento lido, indica-­‐se o número da linha, o número da coluna e o valor. Após a leitura, o valor é atribuído ao elemento que ocupa essa linha e essa coluna. A leitura prossegue até ao fim dos dados. Programe também uma função de teste, que lê primeiro o número de linhas e o número de colunas, cria uma matriz com esse número de linhas e esse número de colunas e depois lê grupos de três números (até ao fim dos dados) por meio da função ints2_get_elements e finalmente escreve a matriz, em colunas de largura 2. Por exemplo, se o ficheiro de entrada for o seguinte: 2 4 1 1 5 0 3 8 1 3 9 Então o ficheiro de saída deve vir assim: 0 0 0 8 0 5 0 9 19. Submeta na tarefa B. 20. Vejamos agora algumas operações sobre matrizes. Primeiro uma operação para trocar duas linhas. O nome da função é ints2_exchange_rows. Os argumentos são a matriz, o número de colunas e os índices das linhas que queremos trocar. 21. Programe uma função de teste que leia primeiro o número de linhas e o número de colunas, depois a matriz (com a função ints2_get) e finalmente uma sequência de pares de números, representando trocas de linhas. No fim, a função escreve a matriz depois de todas as trocas, em colunas de largura 3. Por exemplo, se o ficheiro de entrada for o seguinte: 4 3 1 2 3 4 5 6 7 8 9 10 11 12 0 1 1 2 3 Então o ficheiro de saída deve vir assim: 4 5 6 7 8 9 1 2 3 10 11 12 22. Submeta na tarefa C. 23. A próxima operação é multiplicar todos os elementos de uma linha dada por um valor dado. O nome da função é ints2_multiply_row e os argumentos são a matriz, o número de colunas, o índice da linha e o fator multiplicativo. 24. Antes, programe uma função ints_multiply que, dado um array, o número de elementos e um fator multiplicativo, multiplique cada elemento do array por esse fator. Programe também uma função de teste que processe uma sequência de casos de teste, em número indeterminado. Em cada caso de teste, primeiro vem o número de elementos do array, depois os valores dos elementos e finalmente o fator multiplicativo. A função de teste escreve cada array depois de modificado, usando a função ints_putln, e um espaço como separador. 25. Submeta na tarefa D. 26. Programe agora a função ints2_multiply_row, à base da função ints_multiply. Programe também uma a função de teste análoga função da tarefa C, mas agora o segundo número de cada par é o fator multiplicativo. Por exemplo, se o ficheiro de entrada for o seguinte: 5 3 1 2 3 3 4 5 5 6 7 7 8 9 9 0 1 1 5 4 2 Então o ficheiro de saída deve vir assim: 1 2 3 15 20 25 5 6 7 7 8 9 18 0 2 27. Submeta na tarefa E 28. Queremos agora a operação que consiste em adicionar a uma linha dada outra linha dada, valor a valor. Será a função ints2_add_rows. Os argumento são a matriz, o número de colunas, o índice da primeira linha e ou índice da outra linha. Ao valor de cada elemento da primeira linha adiciona-­‐se o valor do correspondente elemento da outra linha. 29. Tal como no caso anterior, programe antes uma função ints_add que dados dois arrays e o número de elementos de ambos os arrays, adicione o segundo ao primeiro, isto é, que adicione o valor de cada elemento do segundo ao valor do correspondente elemento do primeiro. Programe também uma função de teste 4 que processe uma sequência de casos de teste, em número indeterminado. Em cada caso de teste primeiro vem o número de elementos dos arrays, depois os valores do primeiro array, depois os valores do segundo array. Para cada caso de teste a função de teste escreve o array que “sofreu” a adição, usando a função ints_putln. 30. Submeta na tarefa F. 31. Programe agora a função ints2_add_rows, à base da função ints_add. Programe também uma função de teste análoga à função de teste da tarefa C. 32. Submeta na tarefa G. 33. Surge agora uma operação de outra natureza: a transposição: dada uma matriz m com R linhas e C colunas, construir uma matriz t, com C linhas e R colunas, tal que t[j][i] == m[i][j]. Será a função ints2_transpose_to. Os argumentos são a matriz, o número de linhas, o número de colunas e a matriz resultado. Note que para a matriz resultado não se indica o número de linhas e o número de colunas, porque fica implícito. 34. Programe também a função de teste, que lê uma matriz com ints2_get, depois de ter lido o número de linhas e o número de colunas, transpõe a matriz—isto é, constrói outra matriz que é a transposta da primeira—e escreve a matriz transposta em forma tabular com colunas de largura 3. Por exemplo, se o ficheiro de entrada for o seguinte: 4 3 1 2 3 4 5 6 7 8 9 10 11 12 Então o ficheiro de saída deve vir assim: 1 4 7 10 2 5 8 11 3 6 9 12 35. Submeta na tarefa H. 36. E finalmente, a operação para multiplicar matrizes, que tanto sofrimento causa ao fazer à mão: será a função ints2_multiply. A função terá como argumentos a primeira matriz, respetivos números de linhas e de colunas, a segunda matriz, respetivos números de linhas e de colunas e a matriz que conterá o produto. Para esta, o número de linhas e de colunas não é indicado, pois fica implícito. 37. Antes, programe uma função para calcular o produto interno de dois arrays. Será a função ints_inner_product. Os argumentos são os dois arrays e o número de elementos de ambos. Programe também uma função de teste que processe uma sequência de casos de teste, em número indeterminado. Em cada caso de teste primeiro vem o número de elementos dos arrays, depois os valores do primeiro array, depois os valores do segundo array. Para cada caso de teste a função de teste escreve o valor do produto interno dos dois arrays, um por linha. 38. Submeta na função I. 39. A função de teste para a multiplicação de matrizes lê o número de linhas e o número de colunas da primeira matriz e depois os valores, com inst2_get, e o mesmo para a segunda matriz. A seguir multiplica as duas matrizes, guardando o 5 resultado numa terceira matriz e escreve a esta matriz em forma tabular com colunas de largura 6. Por exemplo, se o ficheiro de entrada for o seguinte: 3 2 1 2 3 4 5 6 2 3 1 5 2 3 1 4 Então o ficheiro de saída deve vir assim: 7 7 10 15 19 22 23 31 34 40. Submeta na tarefa J. Neo: What is the Matrix? Trinity: The answer is out there, Neo, and it's looking for you, and it will find you if you want it to. 6