|
|
LANG |
Autores: Elian de Oliveira e Rodrigo
Pansolim da Rosa
DEFINIÇÃO
D
é uma linguagem de programação de sistemas que se baseia muito na linguagem C. Ela
preenche lacunas que aparecem em algumas linguagens em que seria necessário
buscar auxílio de outros programas externos para preenchê-las.
Ao
programar usando D podem ser vistos aspectos da linguagem Python, Java e C++,
dando a impressão de desenvolver em uma mistura de linguagens, que de maneira
geral é o que acontece. Conceitos como orientação a objetos, paralelismo e
distribuição podem ser feitos em D.
Também
é uma linguagem de alto nível, que reúne o útil com o agradável tornando-o
capaz de produzir programas robustos sendo simples de aprender, com suporte
suficiente para os desenvolvedores, além de trazer conceitos atuais que
linguagens antigas não trazem com elas, pois o poder de computação deve ser
equivalente ao nível de softwares atuais para se obter desempenhos elevados.
TRANSIÇÃO
PARA D
A
transição de C/C++ D deve parecer
natural, uma vez que a semântica é muito parecida, com o visual geral de D sendo
como em C e C++,
assim programadores não precisam começar a aprender do zero, precisando aprender
a princípio as diferenças na sintaxe e como os novos métodos interagem no
desenvolvimento dos códigos.
Já
no decorrer do código não é necessário desenvolver apenas em programação
sequencial, a orientação a objetos também faz parte da linguagem e D possibilita
utilizar ambas no desenvolvimento, além disso, pode-se fazer o tratamento de
exceções para controlar erros e avisos no assim código como em Java.
O uso de
D não significa que o programador ficará restrito a uma MV (virtual machine) especializada em tempo de execução, como o Java.
Não há MV em D, é um compilador simples que gera arquivos de objetos
vinculáveis. D se comunica com o sistema operacional como C. As ferramentas
habituais familiares como makefile se encaixarão
perfeitamente com o desenvolvimento D.
·
A
aparência geral de C/C++ é adotada. Ele usa a mesma sintaxe algébrica, a
maioria das mesmas formas de expressão e declaração, e o layout geral.
·
Os
programas D podem ser escritos em paradigmas familiares, função e dados,
orientação a objetos, modelos de metaprogramação,
funcional, ou qualquer mistura deles.
·
O modelo
de desenvolvimento de compilação/link/ depuração é levado adiante a partir de
linguagens como C, embora nada impeça que D seja compilado e interpretado em bytecode.
·
Uma maior
experiência com tratamento de exceções se mostra uma maneira superior de lidar
com erros do que códigos de erro.
·
D mantém
compatibilidade de link de função com as convenções de chamada C. Isso
possibilita que os programas D acessem diretamente as APIs do sistema
operacional. O conhecimento e a experiência dos programadores com APIs e
paradigmas de programação existentes podem ser levados em consideração.
·
Os
programas D podem sobrecarregar os operadores que permitem a extensão dos tipos
básicos com tipos definidos pelo usuário.
·
O
polimorfismo paramétrico (também conhecido como metaprogramação)
é simples em D.
·
Gerenciamento
personalizado de memória. Há momentos na programação de sistemas em que os
desenvolvedores precisam de alternativas para a coleta de lixo. D também
permite o gerenciamento manual da memória, técnicas como contagem de
referências e o uso de alocadores de memória especializados.
Entre
outros aspectos de transição.
SINTAXES
Algumas
das diferenças que a linguagem D tem não preocupam programadores que já
experimentaram linguagens mais populares, por essa razão a sintaxe que a
linguagem D possui é muito semelhante.
Alguns
aspectos básicos que se destacam são os mostrados a seguir e há outros que, no
decorrer da utilização da linguagem, são associados naturalmente, pois a lógica
utilizada é a mesma para todas as linguagens. Essas são partes básicas que a
linguagem D aborda e para um maior aprofundamento pode ser visto no próprio
site da Dlang (https://dlang.org).
·
import: inclui uma biblioteca, como em Python ou #include
em C;
·
immutable: define as constantes para o programa não sendo
necessário definir o tipo, como #define em C;
·
foreach: um laço de repetição adicional para abranger mais
soluções que foi desenvolvido com a linguagem D;
·
unsigned: para declarar variáveis só positivas é necessário
adicionar a letra u em frente do tipo do dado, no caso de um unsigned int em C ficaria como uint em D;
·
writeln: é muito parecido como o System.out.println
em Java que consegue alternar entre texto e variaveis
no decorrer da função.
PROGRAMAÇÃO
ORIENTADA A OBJETOS
Classes
As
classes são quem dão à linguagem D suporte à orientação a objetos, aspectos
como encapsulamento, herança e polimorfismo estão presentes também e, de maneira
muito similar a estruturas, a maneira de criação e acesso são parecidas, mas há
diferenças notáveis quanto ao utilizar classes. A maior diferença entre ambas é
que estruturas trabalham como tipos de valores e classes são como referências,
outras diferenças entre elas são principalmente devido a esse fato.
O
manuseamento das classes em D é similar à Java, conceitos de criações de novas
classes, comparações, construtores e acessos são facilmente compreendidos,
mostrando como D engloba aspectos importantes de outras linguagens.
Um
exemplo da diferença de declarações de classes e estrutura pode ser visto
abaixo:
import std.stdio; struct MinhaEstrutura
{ int dado; } class MinhaClasse
{ int dado; } void main() { //interação com estrutura MinhaEstrutura
e1; MinhaEstrutura e2
= e1; ++e2.dado; writeln(e1.dado); // mostraria 0 //interação com classe MinhaClasse c1 =
new MinhaClasse; MinhaClasse c2 =
c1; ++c2.dado; writeln(c1.dado); // mostraria 1 } |
Sobrecarga
do operador
A
sobrecarga (overload) consiste em permitir, dentro da
mesma classe, mais de um método com o mesmo nome.
Podem ser
elaboradas classes que trabalham com operadores existentes para ampliar o
sistema de tipos para suportar novos tipos. Um exemplo seria criar uma classe
de grande número e, em seguida, sobrecarregar os +, -, * e / operadores para
permitir o uso de sintaxe algébrica comum com eles.
A
sobrecarga do operador é realizada reescrevendo operadores cujos operandos são
objetos de classe ou estrutura em chamadas para membros especialmente nomeados.
Não é usada sintaxe adicional.
Por
exemplo, a fim de sobrecarregar o operador - (negação) para a estrutura S, e
nenhum outro operador:
struct S { int m; int opUnary(string s)() if (s ==
"-") { return -m; } } int foo(S s) { return -s; } |
PROGRAMAÇÃO
FUNCIONAL
A
programação funcional tem muito a oferecer em termos de encapsulamento,
programação simultânea, segurança de memória e composição. O suporte de D para
programação de estilo funcional inclui:
·
Funções
puras
·
Tipos
imutáveis e estruturas de dados
·
Funções e
fechamentos da Lambda
PRODUTIVIDADE
Módulos
Os
arquivos de origem têm uma correspondência um-para-um com módulos.
Declaração
vs Definição
Funções e
classes são definidas uma vez. Não há necessidade de declarações quando forem
encaminhadas. Um módulo pode ser importado, e todas as suas declarações
públicas ficam disponíveis para o importador.
Exemplo:
class ABC { int func()
{ return 7; } static int z = 7; } int q; |
Todos os
membros são definidos na classe ou na estrutura, não separadamente.
class Foo { int foo(Bar
c) { return c.bar; } } class Bar { int bar() { return 3; } } |
Se uma função
em D está ou não embutida é determinado pelas configurações do otimizador.
Templates
Os templates em D oferecem uma maneira limpa de suportar a
programação genérica, oferecendo o poder da especialização parcial. Os templates de classes e os templates
de funções estão disponíveis, juntamente com templates
de Arrays (Vetores) variados e tuplas.
Matrizes
Associativas
Matrizes associativas
são matrizes com um tipo de dados arbitrário como o índice, em vez de se
limitar a um índice inteiro. Em essência, matrizes associadas são tabelas hash. As matrizes associativas facilitam a construção de
tabelas de símbolos rápidas, eficientes e livres de bugs.
BIBLIOTECA
PHOBOS RUNTIME
Phobos é a biblioteca padrão de tempo de execução que vem
com o compilador de idiomas D.
Geralmente,
o namespace std é usado
para os módulos principais na biblioteca padrão Phobos.
O namespace etc é usado para ligações
externas da biblioteca C/C++. O namespace core é
usado para funções de tempo de execução de baixo nível em D.
A
biblioteca inclui os seguintes módulos: Algoritmos & ranges, Manipulação de
matrizes, Containers, Formatos de dados, Integridade dos dados, Data e hora,
Tratamento de exceção, Ligações externas da biblioteca, Sistema I/O e arquivo,
Interoperabilidade, Gerenciamento de memória, Polimorfismo, Multitarefa, Rede,
Numérico, Manipulações de tipo, Programação vetorial, entre outros.
FUNÇÕES
D tem o
suporte esperado para funções comuns, incluindo funções globais, funções
sobrecarregadas, funções embutidas, funções de membro, funções virtuais,
ponteiros de função, entre outras. Além disso:
Funções
Aninhadas
Funções
podem ser aninhadas dentro de outras funções. Isso é altamente útil para
técnicas de fatoração, localidade e fechamento de funções.
Funções
Literais
Funções
anônimas podem ser incorporadas diretamente em uma expressão.
Fechamentos
dinâmicos
Funções
aninhadas e funções de membro da classe podem ser referenciadas com fechamentos
(também chamados de delegados), tornando a programação genérica muito mais
fácil e de tipo segura.
Parâmetros
In, Out e Ref
Não só
especifica essa ajuda torna as funções mais auto documentadas, como elimina grande
parte da necessidade de ponteiros sem sacrificar nada, e abre possibilidades
para mais ajuda do compilador na busca de problemas de codificação.
Isso
torna possível a comunicação de D diretamente com uma interface e, desta, com
uma variedade mais ampla de APIs estrangeiras. Não haveria necessidade de
soluções alternativas como "Linguagens de Definição de Interface".
VETORES E
MATRIZES
Ambos os conceitos
de vetores e matrizes em linguagem C possuem diferenças quando levadas para o
ambiente D, alguns aspectos são alterados para uma melhor utilização na
linguagem e outros são arrumados e adicionados para completar lacunas
presentes.
As
matrizes C têm várias falhas que podem ser corrigidas:
· As informações das dimensões não são
transportadas com a matriz, e por isso devem ser armazenadas e passadas
separadamente. O exemplo clássico disso são os parâmetros argc
e argv para main(int argc,
char *argv[]). (Em D, o main
é declarado como main(string[] args).)
·
Vetores
não são objetos de primeira classe. Quando um vetor é passado para uma função,
é convertida em um ponteiro, mesmo que o protótipo confusamente diga que é uma
matriz. Quando essa conversão acontece, todas as informações do tipo de vetor
são perdidas.
·
Vetores
em C não podem ser redimensionados. Isso significa que mesmo agregados simples
como uma pilha precisam ser construídos como uma classe complexa.
·
Vetores
em C não podem
ser verificados, porque não sabem quais são os limites do vetor ou matriz.
·
Os
vetores são declarados com o [] após o identificador. Isso leva a uma sintaxe muito
desajeitada para declarar coisas como um ponteiro para uma matriz:
int (*array)[3]; |
Em D, o
[] para a matriz vai à esquerda:
int[3]* array; // declares a pointer to an array
of 3 ints long[] func(int x); // declares a function returning an array of longs |
O que é
muito mais simples de entender.
As matrizes
D vêm em várias variedades: ponteiros, matrizes estáticas, matrizes dinâmicas e
matrizes associativas.
STRINGS
A
manipulação de strings precisa de suporte direto na
linguagem. As linguagens modernas lidam com concatenação de strings,
copias, entre outros, assim como as strings na
linguagem D. Strings são uma consequência direta do
melhor manuseio de vetores.
RANGES(INTERVALOS)
D usa o
conceito de uma range em vez de iteradores ou
geradores encontrados em outras línguas. Um range é qualquer tipo que fornece
uma interface comum a uma sequência de valores. O objetivo de um range é
permitir uma maneira mais simples de escrever códigos que funcionem em dados
arbitrários, tornando-o reutilizável.
O tipo
mais básico de range é chamado de input range(intervalo
de entrada), que fornece três métodos.
struct MyRange { auto front() { // return the next value
in the sequence } void popFront() { // move the
front of the sequence to the
next value } bool empty() { // true if the range has no more values to return } } |
Para
entender o poder desta interface simples, vamos passar por um exemplo. Digamos
que queríamos escrever um programa que contenha todos os funcionários de uma
empresa, excluindo os menores de 40 anos, e agrupamos o restante em um vetor de
vetores de acordo com sua organização.
struct Employee { uint id; uint organization_id; string name; uint age; } struct Employees { Employee[] data; this(Employee[] employees) { data = employees; } Employee front() { return data[0]; } void popFront() { data = data[1
.. $]; } bool empty() { return data.length == 0; } } |
Aqui os
dados vêm de um construtor como um exemplo simples, mas podem vir de qualquer
fonte, como um CSV ou um banco de dados.
Observe
que este código não deve ser usado no código atual, pois em D, o array dinâmico básico também age como um range, de modo que
qualquer algoritmo que aceite ranges também aceita arrays.
No entanto, as matrizes estáticas não são consideradas ranges, pois a operação popFront é baseada na mutação do comprimento do alcance, o
que é impossível com matrizes estáticas. Para obter um alcance de uma matriz
estática, deve-se criar uma fatia contendo todos os seus elementos, assim:
int[4] array
= [1, 2, 3, 4]; // not a range array[]; // valid
range |
Agora que
o intervalo (range) está definido, podemos preenchê-lo e escrever o código de
filtragem.
void main() { import std.algorithm.iteration : filter, chunkBy; Employees employees = Employees([ Employee(1, 1, "George", 50), Employee(2, 3, "John", 65), Employee(3, 2, "David", 40), Employee(4, 1, "Eli", 40), Employee(5, 2, "Hal", 35) ]); auto older_employees
= employees .filter!(a
=> a.age > 40) // lambdas in D use the => syntax .chunkBy!((a,b) => a.organization_id == b.organization_id); } |
Todos os
algoritmos do std.algorithm
trabalham com ranges para evitar o problema de reescrever funcionalidades
comuns para cada projeto. std.algorithm
implementa ordenações, filtros, mapas, reduções e muito mais.
Como a
estrutura de Funcionários está de acordo com a definição do intervalo de
entrada (input range), ela também pode ser usada em foreach
loops, o que detecta automaticamente se o valor passado é um input range.
foreach(employee; employees) { writeln(employee); } |
que é
equivalente a
for(; !employees.empty;
employees.popFront()) { writeln(employees.front); } |
Tipos de
Ranges
O input
range é apenas a forma mais básica de range, há também:
·
forward ranges
·
bidirectional ranges
·
random access ranges
·
output
ranges
Cada um
desses ranges representa uma maneira distinta de acessar os dados subjacentes.
Ou, no caso do intervalo de saída (output range), uma maneira de enviar dados
para outra fonte. Cada um
desses tipos de intervalos lhe dá acesso a diferentes algoritmos na biblioteca
padrão.
ATRIBUTOS
E DECLARAÇÕES DE DEPURAÇÃO
Agora
depurar é parte da sintaxe da linguagem. O código pode ser ativado ou
desativado no tempo de compilação, sem o uso de macros ou comandos de
pré-processamento. A sintaxe de depuração permite um reconhecimento
consistente, portátil e compreensível de que o código-fonte real precisa ser
capaz de gerar compilações de depuração e compilações de lançamento.
TRATAMENTO
DE EXCEÇÃO
O modelo
superior try-catch-finally
é usado em vez de apenas try-catch. Não há necessidade
de criar objetos falsos apenas para que o destruidor implemente a semântica finally.
SINCRONIZAÇÃO
A
programação multithreaded está se tornando cada vez
mais popular, e D fornece primitivas para construir programas com multithreaded. A sincronização pode ser feita tanto no
nível de métodos quanto no nível de objetos.
synchronized int func() { ... } |
As
funções sincronizadas permitem que apenas um segmento por vez esteja executando
essa função.
A
instrução de sincronização coloca um mutex em torno
de um bloco de declarações, controlando o acesso por objeto ou globalmente.
INSTALAÇÃO
E USO
O
compilador do programa D pode ser baixado direto do site Dlang
ou até ser utilizado a partir de uma interface online disponível no site para
programar, se a instalação foi feita o compilador deve estar preparado para
rodar seus programas, que serão compilados pelo terminal do sistema operacional
utilizado. No terminal há três comandos do compilador D prontos, o primeiro é dmd serve para compilação simples do programa gerando um
executável no diretório atual, o segundo é rdmd que
não somente compila mas executa diretamente o programa e o terceiro é dub servindo para um projeto contendo diversos programas de
um todo em um único diretório, então o comando compila todas as dependências e
gera um executável acessado a partir de dub build.
Cada opção de compilação possui suas opções de controle e que podem ser
acessadas a partir de –help sucedendo um dos três comandos.
Exemplos
de código:
// Programa amostra D (peneira.d) /* Sieve
of Eratosthenes prime numbers */ import std.stdio; void main() { size_t count; bool[8191] flags; writeln("10 iterations"); // using iter
as a throwaway variable foreach (iter;
1 .. 11) { count = 0; flags[] = 1; foreach
(index, flag; flags) { if
(flag) { size_t
prime = index + index + 3; size_t
k = index + prime; while
(k < flags.length) { flags[k] = 0; k += prime; } count
+= 1; } } } writefln("%d primes", count); } |
import std.stdio; import std.algorithm; import std.range; void main() { // Let's get going! writeln("Hello World!"); // An example for experienced programmers: // Take three arrays, and without
allocating // any new memory, sort across all the // arrays inplace int[] arr1 = [4, 9, 7]; int[] arr2 = [5, 2, 1, 10]; int[] arr3 = [6, 8, 3]; sort(chain(arr1, arr2, arr3)); writefln("%s\n%s\n%s\n", arr1, arr2, arr3); // To learn more about this example, see the // "Range algorithms"
page under "Gems" } |
//sort
lines import std.stdio, std.array, std.algorithm; void main() { stdin .byLineCopy .array .sort!((a, b) => a > b) // descending order .each!writeln; } |
|
REFERÊNCIA
“Overview”; DLang. Disponível
em: https://dlang.org/overview.html.
Acesso em 19 de outubro de 2020.