Arrays
Há quatro tipos de arrays:
int* p; | Ponteiros para dados |
int[3] s; | Arrays estáticos |
int[] a; | Arrays dinâmicos |
int[char[]] x; | Arrays associativos |
Ponteiros
int* p;Estes são simples ponteiros para dados, análogos aos ponteiros em C. Ponteiros estão disponíveis para interface com C e para trabalhos de sistema especializados. Não há comprimento associado com eles, então não há nenhum modo para o compilador ou tempo de execução fazer checagem de limites, etc., nele. Usos mais convencionais para ponteiros podem ser substituídos com arrays dinâmicos, parâmetros out e inout, e tipos de referência.
Arrays Estáticos
int[3] s;Estes são análogos aos arrays em C. Arrays estáticos são distinto por terem um comprimento fixado em tempo de compilação.
O tamanho total de um array estático não pode exceder 16Mb. Um array dinâmico deveria ser usado para tais arrays grandes.
Um array estático com uma dimensão de 0 é permitido, mas nenhum espaço é alocado para ele. Isso é útil como o último membro de uma estrutura de comprimento variável, eu como o caso de uma expansão de gabarito.
Arrays Dinâmicos
int[] a;Arrays dinâmicos consistem de um comprimento e um ponteiro para os dados do array. Múltiplos arrays dinâmicos podem compartilhar todo ou partes dos dados do array.
Declarações de Arrays
Há dois módos para declarar arrays, prefixa e posfixa. A forma prefixa é o método preferido, especialmente para tipos não-triviais.Declarações de Arrays Prefixas
Declarações prefixas aparecem antes do identificador sendo declarado e são lidas da direita para esquerda, então:int[] a; // array dinâmico de ints
int[4][3] b; // array de 3 arrays de 4 ints cada
int[][5] c; // array de 5 arrays dinâmicos de ints.
int*[]*[3] d; // array de 3 ponteiros para arrays dinâmicos
// de ponteiros para ints
int[]* e; // ponteiro para array dinâmico de ints
Declarações de Arrays Posfixas
Declarações posixas aparecem após o identificador sendo declarado e lidos da esquerda para direita. Cada grupo lista declarações equivalentes:// array dinâmico de intsRazão: A forma posfixa combina com o modo como arrays são declarados em C e C++, e suportarr isso provê uma fácil migração para programadores que usam isso.
int[] a;
int a[];
// array de 3 arrays de 4 ints cada
int[4][3] b;
int[4] b[3];
int b[3][4];
// array de 5 arrays dinâmicos de ints.
int[][5] c;
int[] c[5];
int c[5][];
// array de 3 ponteiros paraarrays dinâmicos de ponteiros para ints
int*[]*[3] d;
int*[]* d[3];
int* (*d[3])[];
// ponteiro para array dinâmico de ints
int[]* e;
int (*e[]);
Uso
Há dois tipos de largas operações para fazer em um array - afetando o controle do array, e afetando o conteúdo do array. C só tem operadores para afetar o controle. Em D, ambos são acessíveis.O controle para um array é especificado nomeando o array, como em p, s ou a:
int* p;
int[3] s;
int[] a;
int* q;
int[3] t;
int[] b;
p = q; // p aponta para a mesma coisa que q.
p = s; // p aponta para o primeiro elemento do array s.
p = a; // p aponta para o primeiro elemento do array a.
s = ...; // erro, já que s é compilado em referência
// estática para um array.
a = p; // erro, já que o comprimento do array apontado
// por p é deesconhecido
a = s; // a é inicializado para apontar para o array s
a = b; // a aponta para o mesmo array que b
Fatiar
Fatiar um array significa especificar um subarray dele. Uma fatia de array não copia os dados, é só outra referência para ele. Por exemplo:int[10] a; // declara array de 10 intsO [] é um atalho para uma fatia do array inteiro. Por exemplo, as atribuições para b:
int[] b;
b = a[1..3]; // a[1..3] é um array de dois elementos consistindo de
// a[1] e a[2]
foo(b[1]); // equivalente à foo(0)
a[2] = 3;
foo(b[1]); // equivalente à foo(3)
int[10] a;são todas semânticamente equivalentes.
int[] b;
b = a;
b = a[];
b = a[0 .. a.length];
Fatiar não é só acessível para referir à partes de outros arrays, mas para converter ponteiros emarrays com checagem de limites:
int* p;
int[] b = p[0..8];
Cópia de Array
Quando o operador de fatia aparece como o lvalue de uma expressão de atribuição, significa que o conteúdo do array são o alvo da atribuição ao invés de uma referência para o array. Cópia de array ocorre quando o lvalue é uma fatia, e o rvalue é um array do ou ponteiro para o mesmo tipo.int[3] s;Sobrepor cópias é um erro:
int[3] t;
s[] = t; // os 3 elementos de t[3] são copiados em s[3]
s[] = t[]; // os 3 elementos de t[3] são copiados em s[3]
s[1..2] = t[0..1]; // o mesmo que s[1] = t[0]
s[0..2] = t[1..3]; // o mesmo que s[0] = t[1], s[1] = t[2]
s[0..4] = t[0..4]; // erro, só 3 elementos em s
s[0..2] = t; // erro, comprimento diferente para lvalue e rvalue
s[0..2] = s[1..3]; // erro, sobrepôs cópiaNão permitir sobreposição torna possível otimização de código paralela mais agressiva que possível com a semântica serial de C.
s[1..3] = s[0..2]; // erro, sobrepôs cópia
Ajuste de Array
Se o operador de fatia aparece como o lvalue de uma expressão de atribuição, e o tipo do rvalue é o mesmo que o tipo do elemento do lvalue, então o conteúdo do array do lvalue é ajustado para o rvalue.int[3] s;
int* p;
s[] = 3; // o mesmo que s[0] = 3, s[1] = 3, s[2] = 3
p[0..2] = 3; // o mesmo que p[0] = 3, p[1] = 3
Concatenação de Array
O operador binário ~ é o operador cat. Ele é usado para concatenar arrays:int[] a;Muitas linguagens sobrecarregam o operador + para significar concatenação. Isso conduz confusamente à fazer:
int[] b;
int[] c;
a = b ~ c; // Cria um array da concatenação dos arrays b e c
"10" + 3produzir o número 13 ou a string "103" como resultado? Iso não é óbvio, e os projetistas da linguagem enrolam cuidadosamente escrevendo regras para diferenciar isso - regras que são impementadas incorretamente, negligenciadas, esquecidas, e ignoradas. É muito melhor ter + significando adição, e um operador separado para ser concatenação de array.
Similarmente, o operador ~= significa anexar, como em:
a ~= b; // a se torna a concatenação de a e b
Concatenação sempre cria uma cópia de
seus
operandos, mesmo se um dos operandos é um array de
comprimento 0
, então:
a = b; // a se refere à b
a = b ~ c[0..0]; // a se refere à uma cópia de b
Operações de Arrays
Quando mais de um operador [] aparece em uma expressão, a escala representada por todos deve combinar.a[1..3] = b[] + 3; // erro, 2 elementos não é o mesmo que 3 elementos
Exemplos:
int[3] abc; // array estático de 3 ints
int[] def = [ 1, 2, 3 ]; // array dinâmico de 3 ints
void dibb(int *array)
{
array[2]; // significa a mesma coisa que *(array + 2)
*(array + 2); // pega segundo elemento
}
void diss(int[] array)
{
array[2]; // ok
*(array + 2); // erro, array não é um ponteiro
}
void ditt(int[3] array)
{
array[2]; // ok
*(array + 2); // erro, array não é um ponteiro
}
Arrays Retangulares
Programadores numéricos FORTRAN experientes sabem que arrays multidimensionais "retangulares" para coisas como operações de matriz são muito mais rápidos que tentar axessá-los via ponteiros para ponteiros resultado de semânticas de "array de ponteiros para array". Por exemplo, a sintaxe D:double[][] matrix;declara matrix como um array de ponteiros para arrays. (Arrays dinâmicos são implementados como ponteiros para os dados do array.) Já que arrays podem ter tamanhos variando (sendo dimensionados dinâmicamente), isso é algumas vezes chamado de arrays "jagged". Even worse for optimizing the code, the array rows can sometimes point to each other! Fortunately, D static arrays, while using the same syntax, are implemented as a fixed rectangular layout:
double[3][3] matrix;declara uma matriz retangular com 3 linhas e 3 colunas, todas continuamente na memória. Em outras linguagens, isso seria chamado de array multidimensional e é declarado como:
double matrix[3,3];
Comprimento do Array
Dentro do [ ] de um array estático ou dinâmico, a variável length é implicitamente declarada e ajustada para o comprimento do array.int[4] foo;
int[] bar = foo;
int* p = &foo[0];
// Estas expressões são todas equivalentes:
bar[]
bar[0 .. 4]
bar[0 .. length]
bar[0 .. bar.length]
p[0 .. length] // 'length' não definido, já que p não é um array
bar[0]+length // 'length' não definido, fora do escopo de [ ]
bar[length-1] // pega último elemento do array
Propriedades de Arrays
Propriedades de arrays estáticos são:.sizeof | Retorna o comprimento do array multiplicado pelo número de bytes por elemento do array. |
.length | Retorna o número de elementos no array. Essa é uma quantidade fixa para arrays estáticos. |
.ptr | Retorna um ponteiro para o primeiro array. |
.dup | Cria um array dinâmico do mesmo tamanho e copia os conteúdos do array nele. |
.reverse | Reverte no lugar a ordem dos elementos no array. Retorna o array. |
.sort | Ordena no lugar a ordem dos elementos no array. Retorna o array. |
Propriedades de arrays dinâmicos são:
.sizeof | Retorna o tamanho da referência do array dinâmico, que é 8 em máquinas de 32 bits. |
.length | Pega/ajusta o número de elementos no array. |
.ptr | Retorna um ponteiro para o primeiro elemento do array. |
.dup | Cria um array dinâmico do mesmo tamanho e copia os conteúdos do array nele. |
.reverse | Reverte no lugar a ordem dos elementos no array. Retorna o array. |
.sort | Ordena no lugar a ordem dos elementos no array. Retorna o array. |
Exemplos:
p.length // erro, comprimento não conhecido para ponteiros
s.length // contante de tempo de compilação 3
a.length // valor de tempo de execução
p.dup // erro, comprimento não conhecido
s.dup // cria um array de 3 elementos, copia
// elementos de s nele
a.dup // cria um array de a.length elementos, copia
// elementos de a nele
Ajustando o Comprimento de Array Dinâmico
A propriedade .length de um array dinâmico pode ser ajustada como o lvalue de um operador =:array.length = 7;Isso faz o array ser realocado no lugar, e o conteúdo existente copiado sobre o novo array. Se o comprimento do novo array é menor, só o bastante é copiado para preencher o novo array. Se o comprimento do novo array é maior, o restante é preenchido com o inicializador padrão.
Para maximizar eficiência, o tempo de execução sempre tenta redimensionar o array no lugar para evitar cópias extras. Isso sempre fará uma cópia se o novo tamanho é maior e o array não foi alocado via o operador new ou operação de redimensionamento prévia.
Isso significa que se há uma fatia de array imediatamente seguindo o array sendo redimensionado, o array redimensionado poderia sobrepôr a fatia, isto é:
char[] a = new char[20];Para garantir comportamento de cópia, use a propriedade .dup para assegurar um array original que pode ser redimensionado.
char[] b = a[0..10];
char[] c = a[10..20];
b.length = 15; // sempre redimensionado no,lugar pois é fatiado
// de a[] que tem bastante memória para 15 chars
b[11] = 'x'; // a[15] e c[5] também são afetados
a.length = 1;
a.length = 20; // nenhuma mudança líquida na disposição de memória
c.length = 12; // sempre faz uma cópia pois c[] não está no
// início de um bloco de alocação do gc
c[5] = 'y'; // não afeta conteúdo de a[] ou b[]
a.length = 25; // pode ou não fazer cópia
a[3] = 'z'; // pode ou não afetar b[3] que ainda sobrepões
// o velho a[3]
Esses assuntos tambés se aplicam à concatenar arrays com os operadores ~ e ~=.
Redimensionar um array dinâmico é uma operação relativamente cara. Então, enquano o seguinte método de preencher um array:
int[] array;trabalha, é ineficiênte. Uma aproximação mais prática seria minimizar o número de redimensionamentos:
while (1)
{ c = getinput();
if (!c)
break;
array.length = array.length + 1;
array[array.length - 1] = c;
}
int[] array;Escolher uma boa suposição inicial é uma arte, mas você normalmente pode escolher um valor cobrindo 99% dos casos. Por exemplo, quando recolher entrada do usuário do console - é inprovável que seja maior que 80.
array.length = 100; // suposição
for (i = 0; 1; i++)
{ c = getinput();
if (!c)
break;
if (i == array.length)
array.length = array.length * 2;
array[i] = c;
}
array.length = i;
Checagem de Limites do Array
É um erro indexar um array com um índice que é menor que 0 ou maior que ou igual ao comprimento do array. Se um índice está fora dos limites, uma exceção ArrayBoundsError é lançada se detectado em tempo de execução, e um erro se detextado em tempo de compilação. Um programa não pode confiar no acontecimento de checagem de limites do array, por exemplo, o seguinte programa está incorreto:tryO laço é corretamente escrito:
{
for (i = 0; ; i++)
{
array[i] = 5;
}
}
catch (ArrayBoundsError)
{
// termina laço
}
for (i = 0; i < array.length; i++)Nota de Implementação: Compiladores deveriam tentar detecar erros de limites de array em tempo de compilação, por exemplo:
{
array[i] = 5;
}
int[3] foo;Inserção de código de checagem de limites de array em tempo de compilação deveria ser ligada e desligada com um interruptor de tempo de compilação.
int x = foo[3]; // erro, fora dos limites
Inicialização de Array
Inicialização Padrão
- Ponteiros são inicializados para null.
- Conteúdos de arrays estáticos são inicializados para o inicializador padrão para o tipo de elemento do array.
- Arrays dinâmicos são inicializados tendo 0 elementos.
- Arrays associativos são inicializados tendo 0 elementos.
Inicialização Vazia
Inicialização vazia ocorre quando o Inicializador para um array é void. O que isso significa é que nenhuma inicialização é feita, isto é, os conteúdos do array serão indefinidos. Isso é mais útil como uma otimização de eficiência. Inicializações vaizas são uma técnica avançada e deveriam somente ser usadas quando perfilar indica que isso importa.Inicialização Estática de Arrays Estáticos
int[3] a = [ 1:2, 3 ]; // a[0] = 0, a[1] = 2, a[2] = 3Isso é mais acessível quando os índices do array são dados por enums:
enum Color { red, blue, green };Se qualquer elemento do array é inicializado, todos devem ser. Isso é para capturar erros comuns onde outro elemento é aicionado à um enum, mas uma das instâncias estáticas dos arrays daquele enum foi negigenciado em atualizar a lista do inicializador.
int value[Color.max + 1] = [ Color.blue:6, Color.green:2, Color.red:5 ];
Tipos Especiais de Arrays
Strings
Linguagens deveriam ser boas em controlar strings. C e C++ não são bons nisso. As dificuldades primárias são gerenciamento de memória, controle de temporários, constantemente re-varrer a string procurando pelo 0, e os arrays fixos.Arrays dinâmicos em D sugerem a solução óbvia - uma string é apenas um array dinâmico de caracteres. Literais de string tornam-se apenas um fácil modo para escrever arrays de caracteres.
char[] str;Strings char[] estão no formato UTF-8. Strings wchar[] estão no formato UTF-16. Strings dchar[] estão no formato UTF-32.
char[] str1 = "abc";
Strings podem ser copiadas, comparadas, concatenadas e anexadas:
str1 = str2;com as semânticas óbvias. Quaisquer temporarios gerados são limpos pelo coletor de lixo (ou usando alloca()). Não só isso, isso trabalha com qualquer array, não só com um array string especial.
if (str1 < str3) ...
func(str3 ~ str4);
str4 ~= str1;
Um ponteiro para caracter pode ser gerado:
char *p = &str[3]; // aponta para o 4º elementoJá que strings, porém, não são terminadas em 0 em D, quando transferir um ponteiro para uma string para C, adicione um terminador 0:
char *p = str; // aponta para o 1º elemento
str ~= "\0";O tipo de uma string é determinado pela fase semântica de compilação. O tipo é um de: char[], wchar[], dchar[], e é determinado por regras de conversão implítita. Se há duas conversões implícitas igualmente aplicáveis, o resultado é um erro. Para diferenciar nesses casos, um molde é apropriado:
cast(wchar [])"abc" // isso é um array de caracteres wcharLiterais de string são implicitamente convertidos entre chars, wchars, e dchars como necessário.
Strings de um único caracter de comprimento podem também ser convertidas para uma constante char, wchar ou dchar:
char c;
wchar w;
dchar d;
c = 'b'; // a c é atribuído o caracter 'b'
w = 'b'; // a w é atribuído o caracter wchar 'b'
w = 'bc'; // erro - somente um caracter wchar por vez
w = "b"[0]; // a w é atribuído o caracter wchar 'b'
w = \r; // a w é atribuído o caracter wchar de retorno de carro
d = 'd'; // a d é atribuído o caracter 'd'
printf() e Strings
printf() é uma função C e não é parte de D. printf() imprimirá strings C, que são terminadas em 0. Há dois modos de usar printf() com strings D. A primeira é adicionar um terminador 0, e moldar o resultado para um char*:str ~= "\0";O segundo modo é usar o especificador de precisão. Do modo como arrays D são dispostos, o comprimento vêm primeiro, então o seguinte trabalha:
printf("the string is '%s'\n", (char *)str);
printf("the string is '%.*s'\n", str);No futuro, pode ser necessário adicionar um novo especificador de formato para printf() ao invés de confiar em um detalhe dependente de implementação.
Conversões Implícitas
Um ponteiro T* pode ser implicitamente convertido para um dos seguintes:- U* onde U é uma classe base de T.
- void*
- T*
- T[]
- U[dim] onde U é uma classe base de T.
- U[] onde U é uma classe base de T.
- U* onde U é uma classe base de T.
- void*
- void[]
- T*
- U[] onde U é uma classe base de T.
- U* onde U é uma classe base de T.
- void*
Arrays Associativos
D vai uma etapa adiante com arrays - adicionando arrays associativos. Arrays associativos tem um índice que não é necessariamente um inteiro, e pode ser escassamente povoada. O índice para um array associativo é chamado chave, e seu tipo é chamado TipoChave.Arrays associativos são declarados substituindo o TipoChave dentro do [] de uma declaração de array:
int[char[]] b; // array associativo b de ints que éChaves particulares em um array associativo podem ser removidas com a função remove:
// indexado por um array de caracteres.
// O TipoChave é char[]
b["hello"] = 3; // ajusta o valor associado com a chave "hello" para 3
func(b["hello"]); // passa 3 como parâmetro para func()
b.remove("hello");A InExpression rende um ponteiro para o valor se a chave está no array associativo, ou null se não:
int* p;TiposChave não podem ser funções ou voids.
p = ("hello" in b);
if (p != null)
...
Se o TipoChave é um tipo de estrutura, um mecânismo padrão é usado para computar o hash e comparações dele baseadas nos dados binários dentro do valor da estrutura. Um mecânismo customizado pode ser usado provendo as seguintes funções como membros da estrutura:
uint toHash();Por exemplo:
int opCmp(KeyType* s);
import std.string;
struct MyString
{
char[] str;
uint toHash()
{ uint hash;
foreach (char c; s)
hash = (hash * 9) + c;
return hash;
}
int opCmp(MyString* s)
{
return std.string.cmp(this.str, s.str);
}
}
Propriedades
Propriedades para arrays associativos são:.sizeof | Retorna o tamanho da referência para o array associativo; é tipicamente 8. |
.length | Retorna o número de valores no array associativo. Diferente de arrays dinâmicos, é somente leitura. |
.keys | Retorna um array dinâmico, os elementos dele são as chaves no array associativo. |
.values | Retorna um array dinâmico, os elementos dele são os valores no array associativo. |
.rehash | Reorganiza o array associativo no lugar de modo que buscas sejam mais eficiêntes. rehash é efetivo quando, por exemplo, o programa fica pronto carregando uma tabela de símbolos e agora precisa de buscas rápidas nela. Reotrna uma referência para o array reorganizado. |
Exemplo de Array Associativo: word count
import std.file; // D file I/O
int main (char[][] args)
{
int word_total;
int line_total;
int char_total;
int[char[]] dictionary;
printf(" lines words bytes file\n");
for (int i = 1; i < args.length; ++i) // argumentos do programa
{
char[] input; // buffer de entrada
int w_cnt, l_cnt, c_cnt; // contadores de palavram linha e caracter
int inword;
int wstart;
input = std.file.read(args[i]); // lê arquivo em input[]
foreach (char c; input)
{
if (c == '\n')
++l_cnt;
if (c >= '0' && c <= '9')
{
}
else if (c >= 'a' && c <= 'z' ||
c >= 'A' && c <= 'Z')
{
if (!inword)
{
wstart = j;
inword = 1;
++w_cnt;
}
}
else if (inword)
{ char[] word = input[wstart .. j];
dictionary[word]++; // incrementa contador para a palavra
inword = 0;
}
++c_cnt;
}
if (inword)
{ char[] word = input[wstart .. input.length];
dictionary[word]++;
}
printf("%8ld%8ld%8ld %.*s\n", l_cnt, w_cnt, c_cnt, args[i]);
line_total += l_cnt;
word_total += w_cnt;
char_total += c_cnt;
}
if (args.length > 2)
{
printf("-------------------------------------\n%8ld%8ld%8ld total",
line_total, word_total, char_total);
}
printf("-------------------------------------\n");
char[][] keys = dictionary.keys; // procura todas palavras em dictionary[]
for (int i = 0; i < keys.length; i++)
{ char[] word;
word = keys[i];
printf("%3d %.*s\n", dictionary[word], word);
}
return 0;
}