Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Username: Password: oppure
Recensione D programming language - Software

D programming language

A cura di: Netarrow


Immagine postata dall'utente


Cosè il D?

Il D è un linguaggio di programmazione ideato dalla
DigitalMars(http://www.digitalmars.com/), il suo
obiettivo è raggiungere la potenza e le alte prestazioni dei
linguaggi di medio livello come C e C++ ma con la grande
produttività e semplice portabilità di linguaggi di alto livello
come C#, Java, Python, Ruby e simili.
In particolare la sintassi e le caratteristiche tecniche del
compilatore riprendono C/C++, C# e Java. Se volete una
comparazione schematica fra D e questi altri linguaggi visitate
questa pagina del sito della digitalmars:
http://www.digitalmars.com/d/comparison.html


Caratteristiche del compilatore D


Passiamo ora ad un analisi più approfondita di questo linguaggio,
partiamo dal compilatore che trovate qui:
http://www.digitalmars.com/d/dcompiler.html.
Le caratteristiche più importanti le elenchiamo qui di seguito:

  • Veloce a compilare, più di quello Java e C++
  • Utilizzo della garabage collection simile a .net e
    java, ma a quanto pare più veloce
  • si basa su una libreria detta PHOBOS ben fatta(supporta
    sockets, thread, zip, base64, funzioni linux, funzioni windows,
    md5 e altro)inoltre supporta la libreria C
    direttamente
  • Il compilatore riconosce l'html presente nel codice
    sorgente ignorandolo e permettendo comunque la
    compilazione
  • Nonostante le somiglianze con C# e Java non necessita
    di installare separatamente degli interpreti, tutto il necessario
    sta nel binario
    senza che assuma dimensioni spropositate
  • C'è un compilatore per win32 e linux su x86, ma su
    sourgeforge c'è un progetto detto GDC;
    http://dgcc.sourceforge.net/ che supporta anche altri
    OS
  • Il codice supporta i codici stile sh, quindi i nostri
    sorgenti possono cominciare con su unix con #! /usr/bin/dmd -run per esempio

Ecco queste sono alcune caratteristiche del compilatore, non sono
tutte ovviamente delle altre le vedremo più avanti trattando
anche delle caratteristiche che influenzano la sintassi e il
comportamento del programmatore.


Cartteristiche del D


Ora vedremo delle caratteristiche a livello di codice, alcune
molto interessanti anche dal punto di vista del confronto fra
C/C++ C# e Java. Cominciamo dalle funzioni che solo il D
supporta:


Nested Functions


Con questa caratteristica una funzione può contenere a sua volta
delle dichiarazioni di altre funzioni, ovviamente entra il gioco
la regola dello scope per potere richiamare, scriviamo un
esempio:

void foo()
{
   void A()
   {
     B();   // ok
     C();   // error, C undefined
   }
   void B()
   {
       void C()
       {
           void D()
           {
               A();      // ok
               B();      // ok
               C();      // ok
               D();      // ok
           }
       }
   }
   A(); // ok
   B(); // ok
   C(); // error, C undefined
}


Potete notare che la funzione più interna ha accesso a tutte
quelle esterne, ma una funzione estarna non può accedere alle
funzioni interne di una sua funzione interna, una sorta di pseudo
incapsulamento ma senza classi. Caratteristica importante è che
l'ordine delle funzioni è fondamentale, se noi scriviamo questo:


void test()
{
    void foo() { bar(); }    // error, bar not defined
    void bar() { foo(); }    // ok
}


ci accorgiamo che non possono richiamarsi le funzioni
reciprocamente visto che le funzioni che vengono prima non
possono chiamare quelle che vengono dopo(come in Turbo Pascal),
la soluzione in D è usare i delegate, che sono supportati:

void test()
{
    void delegate() fp;
    void foo() { fp(); }
    void bar() { foo(); }
    fp = &bar;
}


Ad ogni modo sul sito della digitalmars si vede che questa
restrizione verrà rimossa molto probabilmente.

Un'altra caratteristica è che una funzione nested può accedere ai
campi di quella estarna, se la funzione è static anche il campo
deve essere static.

Se dentro la funzione creiamo una che contiene una funzione con
un'altra dentro, queste funzioni potranno accedere solo ai campi static.


void test()
{   int j;
    static int s;

    struct Foo
    {   int a;

    int bar()
    {   int c = s;        // ok, s is static
        int d = j;        // error, no access to frame of test()

        int foo()
        {
        int e = s;    // ok, s is static
        int f = j;    // error, no access to frame of test()
        return c + a;    // ok, frame of bar() is accessible,
                // so are members of Foo accessible via
                // the 'this' pointer to Foo.bar()
        }
    }
    }
}



Function Literals


Questo permette di creare un puntatore a funzione o un delegato
direttamente in un'espressione, quindi invece di fare così:

int function(char c) fp;    // declare pointer to a function

void test()
{
    static int foo(char c) { return 6; }

    fp = &foo;
}


si può fare così:

int function(char c) fp;

void test()
{
    fp = function int(char c) { return 6;} ;
}


Questo vale anche per i delegate, quindi si può utilizzare questo
codice:

double test()
{   double d = 7.6;
    float f = 2.3;

    void loop(int k, int j, void delegate() statement)
    {
    for (int i = k; i < j; i++)
    {
        statement();
    }
    }

    loop(5, 100, delegate { d += 1; } );
    loop(3, 10,  delegate { f += 1; } );

    return d + f;
}




Resiziable array


tramite il campo length è possibile accedeer alla dimensione di
un array e ridimensionarlo, es:

int[] array;
while (1)
{   c = getinput();
    if (!c)
       break;
    array.length = array.length + 1;
    array[array.length - 1] = c;
}



Slicing


Permette di creare un nuovo riferimento
a un subarray, la sintassi è questa:

int[10] a;    // declare array of 10 ints
int[] b;

b = a[1..3];    // a[1..3] is a 2 element array consisting of
        // a[1] and a[2]
foo(b[1]);    // equivalent to foo(0)
a[2] = 3;
foo(b[1]);    // equivalent to foo(3)



Associative arrays


Permette di associare il valore di un elemento di un array ad un
altro tipo di dato, diverso dal calssimo intero, quindi potremo
avere:

int[char[]] b;        // associative array b of ints that are
            // indexed by an array of characters.
            // The KeyType is char[]
b["hello"] = 3;        // set value associated with key "hello" to 3
func(b["hello"]);    // pass 3 as parameter to func()
...
int* p;
p = ("hello" in b);
if (p != null)
 printf(*p);
b.remove("hello");
...



Strong typedefs


Creando questo:

typedef int myint;

se dichiaro una funzione che accetta un int, posso crearne una
che accetta un myint senza che venga dato un errore, inoltre
possa dare un'inizializzazione di default con


typedef int myint = 9718;
myint i; //inizializzato a 9718

usando invece alias:

alias int myint;

viene dato un errore se una funzione con lo stesso nome accetta
prima un int e dopo un myint.


Mixin


Molto interessante, permette di ottenere un set di dichiarazioni
da un template specificato e metterle da qualche altra parte:


template Foo()
{
    int x = 5;
}

mixin Foo;

struct Bar
{
    mixin Foo;
}

void test()
{
    printf("x = %dn", x);        // prints 5
    {   Bar b;
    int x = 3;

    printf("b.x = %dn", b.x);    // prints 5
    printf("x = %dn", x);        // prints 3
    {
        mixin Foo;
        printf("x = %dn", x);    // prints 5
        x = 4;
        printf("x = %dn", x);    // prints 4
    }
    printf("x = %dn", x);        // prints 3
    }
    printf("x = %dn", x);        // prints 5
}


Questo vale anche per le funzioni:

template Foo()
{
    void func() { printf("Foo.func()n"); }
}

class Bar
{
    mixin Foo;
}

class Code : Bar
{
    void func() { printf("Code.func()n"); }
}

void test()
{
    Bar b = new Bar();
    b.func();        // calls Foo.func()

    b = new Code();
    b.func();        // calls Code.func()
}


posso specificare il tipo del template

template Foo(T)
{
    T x = 5;
}

mixin Foo!(int);        // create x of type int


e infine un esempio finale:

template duffs_device(alias id1, alias id2, alias s)
{
    void duff_loop()
    {
    if (id1 < id2)
    {
        typeof(id1) n = (id2 - id1 + 7) / 8;
        switch ((id2 - id1) % 8)
        {
        case 0:        do {  s();
        case 7:              s();
        case 6:              s();
        case 5:              s();
        case 4:              s();
        case 3:              s();
        case 2:              s();
        case 1:              s();
                  } while (--n > 0);
        }
    }
    }
}

void foo() { printf("foon"); }

void test()
{
    int i = 1;
    int j = 11;

    mixin duffs_device!(i, j, delegate { foo(); } );
    duff_loop();    // executes foo() 10 times
}



Static If Condition


Un if eseguito a compile time, sostituisce la direttive di
preprocessore praticamente:

const int i = 3;
int j = 4;

static if (i == 3)    // ok, at module scope
    int x;

class C
{   const int k = 5;

    static if (i == 3)    // ok
    int x;
    else
    long x;

    static if (j == 3)    // error, j is not a constant
    int y;

    static if (k == 5)    // ok, k is in current scope
    int z;
}

template INT(int i)
{
    static if (i == 32)
    alias int INT;
    else static if (i == 16)
    alias short INT;
    else
    static assert(0);    // not supported
}

INT!(32) a;    // a is an int
INT!(16) b;    // b is a short
INT!(17) c;    // error, static assert trips


come si può vedere di conseguenza c'è anche l'assert statico.


Identity Expressions


Permette di controllare le identità di due variabili con
l'operatore is o !is(l'opposto).
Quindi controlla, ad esempio per le classi, se sono la stessa
istanza, per gli array se contengono gli stessi elementi ecc...


Implicit Type Inference


usando auto si può rendere implicito il tipo di una variabile,
es:

static x = 3;       // x is type int
auto y = 4u;       // y is type uint
auto s = "string"; // s is type char[6]

class C { ... }

auto c = new C();  // c is a handle to an instance of class C



Contract Programming


La programmazione per contratto... ottima per evitare bugs, viene
contrallata una condizione in entrata alla funzione e in uscita,
quindi si controlla un requisito per eseguire il corpo, e si
ritorna il risultato solo se , subito un esempio, la troviamo
anche in altri linguaggi come l'Eiffel:

long square_root(long x)
    in
    {
    assert(x >= 0);
    }
    out (result)
    {
    assert((result * result) == x);
    }
    body
    {
    return math.sqrt(x);
    }



Unit test


Quante volte avete creato la classe e volevate testarla? E allora
scrivi un main, richiama, nuova funziona, magari nuovo file
ricompila, sistema ecc...
Con D basta fare:

class Sum
{
    int add(int x, int y) { return x + y; }

    unittest
    {
    Sum sum = new Sum;
    assert(sum.add(3,4) == 7);
    assert(sum.add(-2,0) == -2);
    }
}

void main() {
}


e compilare con l'opzione -unittest


Scope Statement


Serve per eseguire una funzione all'uscita di uno
scope(indipendentemente dal fatto che ci siano o meno errori),
all'uscita con errori, e all'uscita senza errori, ecco un
esempio(da notare che vengono eseguiti all'inverso, dal basso
verso l'alto):

Un esempio semplice è questo:

writef("1");
{
    writef("2");
    scope(exit) writef("3");
    scope(exit) writef("4");
    writef("5");
}
writefln(); 


che stampa 4321

un esempio un pò più comp'lesso è:

class Foo
{
    this() { writef("0"); }
    ~this() { writef("distrutto"); }
}

try
{
    scope(exit) writef("2");
    scope(success) writef("3");
    auto Foo f = new Foo();
    scope(failure) writef("4");
    throw new Exception("msg");
    scope(exit) writef("5");
    scope(success) writef("6");
    scope(failure) writef("7");
}
catch (Exception e)
{
}
writefln();


che stampa 0412


Struct member alignment control


Permette di specificare l'allineamento dei membri di una
struttura, con una sintassi simile a questa:


align (1) struct S
{   byte a;    // placed at offset 0
    byte[3] filler1;
    byte b;    // placed at offset 4
    byte[3] filler2;
}



Utilità e risorse per il D


Apparte il compilatore è possibile scaricare molti IDE dal sito
http://www.dsource.org o c'è una lista anche qui:
http://www.prowiki.org/wiki4d/wiki.cgi?EditorSupport,
quello che utilizzo io non si trova fra quelli, è in realtà un
editor di testo multi linguaggio, si chiama Crismon Editor, lo
trovare qui: http://www.crimsoneditor.com/, ecco una
screen:

Immagine postata dall'utente


Conclusioni


Il linguaggio promette veramente bene, sintassi del C/C++ con
grandi miglioramenti(quello più banale è che le stringhe sono
finalmente un tipo base), ci sono tante altre novità o curiosità sul modo di implementare delle funzioni ma questa non è una guida.
Consiglio di provarlo e diffondere il linguaggio, in modo che possa essere più usato; inoltre fra i link degli IDE c'è anche un
plugin per il Visual Studio che supporta il D.


Fonti e ringraziamenti


Grazie all'utente kentaromiura del forum html.it per le pillole
sul D, dalle quali sono state tratte alcune informazioni.

Tutti i sorgenti e alcune informazioni sono stati tratti dal sito
della digitalmars e qualcosa da dsource.org.
Per chi è interessato esiste un libro sul di: "D programming
language perfect guide" però è... in giapponese :-) dei tutorial
li trovate su dsource e digitalmars.