Pular para o conteúdo

Arrays

Os arrays no Kriol são colecções de tamanho fixo de elementos do mesmo tipo.

Serve para quando tens muitos valores da mesma espécie: em vez de nota1, nota2, nota3, tens um nota com um índice.

<tipo>[<tamanho>] <nome> = [<valor1>, <valor2>, ...];
int[3] nums = [10, 20, 30];
bool[2] flags = [sin, nau];

O número entre [ ] faz parte do tipo, não do nome: int[3] lê-se “array de 3 inteiros”. Tem de ser um literal, e o número de valores entre [ ] tem de corresponder exactamente.

O literal [ ... ] sem tipo explícito é interpretado pelo contexto da variável ou campo que o recebe. Quando o contexto não deixa claro o tipo do array, usa um literal tipado, escrevendo o tipo dos elementos seguido de [] entre parênteses:

int[3] valores = (int[]) [1, 2, 3];

Para preencher um array com o mesmo valor em todas as posições, usa a sintaxe [valor; N], em que N é o número de elementos:

int[5] zeros = [0; 5];
num[10] uns = [1.0; 10];

O N tem de ser igual ao tamanho declarado do array. É a forma habitual de criar um array que vais preencher depois num ciclo, já que no Kriol nenhuma variável fica sem valor inicial.

Os índices começam em 0:

int[5] seq = [1, 2, 3, 4, 5];
mostran(seq[0]); // 1
mostran(seq[4]); // 5
seq[2] = 99;
mostran(seq[2]); // 99

Num array de N elementos, o último índice válido é N - 1, e é por isso que um ciclo sobre um array se escreve i < N e não i <= N.

O índice pode ser qualquer expressão de tipo inteiro, com ou sem sinal, mas não pode ser um real:

int[3] v = [1, 2, 3];
int i = 1;
u8 j = 2;
mostran(v[i + 1]); // 3
mostran(v[j]); // 3
// mostran(v[1.0]); // ERRO: o índice de um array tem de ser um número inteiro

Um índice fora do array termina o programa com uma mensagem que indica a linha, o índice e os limites:

programa.kriol:5: erro de execução: o índice 3 está fora do array, que vai de 0 a 2

Essa verificação acontece em execução, a cada acesso, mesmo quando o índice é um literal: v[7] num int[3] compila sem queixas e falha ao correr. Em troca, nunca lês nem escreves memória que não é tua, ao contrário do que acontece em C.

int[10] seq = [0; 10];
pa int i = 0; i < 10; i += 1 {
seq[i] = i * i;
}
mostran(f"Quadrados: {seq}");

O tamanho não se pode perguntar ao array: não há seq.tamanho nem konta(seq). Como é fixo e conhecido na compilação, o costume é guardá-lo numa variável de topo, como int tamanho = 10;, e usá-la no ciclo. Note-se que o nome não pode ser TAMANHO: começar por maiúscula está reservado para moldes, por isso não há a convenção de constantes em maiúsculas de outras linguagens.

Um array pode ter várias dimensões. Como em C e em Go, o primeiro tamanho é a dimensão de fora: int[2][3] tem 2 linhas de 3 elementos.

int[2][3] m = [[1, 2, 3], [4, 5, 6]];
int[3][2] zeros = [[0; 2]; 3]; // 3 linhas de [0, 0]
mostran(m[1][2]); // 6
zeros[2][1] = 9;
pa int i = 0; i < 2; i += 1 {
pa int j = 0; j < 3; j += 1 {
mostra(m[i][j]);
}
}

Cada linha é ela própria um array (m[1] é um int[3]), que se pode copiar, imprimir ou passar a uma função:

m[0] = m[1]; // copia a linha 1 para a linha 0
int[3] linha = m[1]; // uma cópia da linha
mostran(m); // [[4, 5, 6], [4, 5, 6]]

Como os moldes, os arrays são valores: atribuir, passar a uma função ou devolver um array copia-o por inteiro.

fn dobra(int[3] v) int[3] {
pa int i = 0; i < 3; i += 1 {
v[i] *= 2; // altera a cópia local
}
divolvi v;
}
fn inisiu() {
int[3] a = [1, 2, 3];
int[3] b = dobra(a); // a continua [1, 2, 3]
a = dobra(a); // atribuição do array inteiro
a = [a[2], a[1], a[0]]; // o novo valor é construído antes de ser guardado
mostran(dobra([5; 3])[2]); // 10
}

Isto distingue o Kriol do C, onde um array passado a uma função é um ponteiro e a função altera o original. Aqui é mais fácil de prever, mas copiar um array grande a cada chamada leva tempo e ocupa pilha.

O tamanho faz parte do tipo, e os tipos têm de ser iguais: um int[3] só aceita outro int[3], e não um int[4] nem um u8[3]. Um literal [ ... ] ou [valor; N] também pode ser usado diretamente como argumento, valor de divolvi, valor de uma atribuição ou valor depois de sinon, e recebe o tipo do destino.

Para alterar um array de quem chama, devolve o novo valor e atribui-o, como em a = dobra(a);.

Podes passar um array diretamente a mostra / mostran ou usá-lo numa f-string para imprimir todos os elementos:

int[3] a = [1, 2, 3];
mostran(a); // [1, 2, 3]
mostran(f"Array: {a}"); // Array: [1, 2, 3]

Os elementos aparecem entre [ ], separados por vírgula e espaço, e cada um é escrito como se fosse impresso sozinho: um num[2] sai [1.5, 2], um bool[2] sai [sin, nau] e um int[2][2] sai [[1, 2], [3, 4]].

Arrays também podem aparecer como campos de um molde:

molda Pessoa {
textu nomi;
int[2] flags;
}
fn inisiu() {
Pessoa pessoa = Pessoa{nomi = "jose", flags = [0, 1]};
mostran(f"Flags: {pessoa.flags}"); // Flags: [0, 1]
}

Arrays de valores de moldes podem ser guardados e indexados. Isto permite encadear acesso a índice e campo:

molda Enderesu {
textu rua;
int numero;
}
molda Pessoa {
Enderesu[2] enderesus;
}
fn inisiu() {
Pessoa pessoa = Pessoa{
enderesus = [
Enderesu{rua = "antigu", numero = 10},
Enderesu{rua = "nobu", numero = 44}
]
};
pessoa.enderesus[0].rua = "medio";
pessoa.enderesus[0].numero += 5;
mostran(pessoa.enderesus[0].rua); // medio
mostran(pessoa.enderesus[0].numero); // 15
}
  • O tamanho do array deve ser um literal inteiro. Não pode ser uma variável.
  • Não há arrays de tamanho variável, nem forma de crescer ou encurtar um array.
  • O array não guarda o seu tamanho: não há tamanho nem konta().
  • Um array declarado dentro de uma função fica na pilha e pode ocupar no máximo 1 MiB (por exemplo, 131 072 valores int). Arrays maiores devem ser declarados fora das funções; vê Tamanho das variáveis locais.
  • Aceder a um índice fora do array termina o programa com uma mensagem de erro que indica a linha e o índice.
  • Literais sem tipo explícito precisam de um contexto de array. Se o compilador não conseguir inferir o alvo, usa (tipo[]) [...].
  • Arrays de valores de moldes podem ser usados, mas não podem ser impressos como um todo com mostra, mostran ou f-strings; imprime campos individuais ou arrays de tipos primitivos.
  • Um array passado a uma função ou devolvido por ela é copiado, por isso o limite de 1 MiB aplica-se também aos parâmetros e aos tipos de retorno.
  • Cada dimensão pode ter no máximo 1 048 576 elementos.