Questo post descriverà le basi di una funzionalità molto potente del linguaggio di programmazione D: la Compile-time Function Execution
La Compile-time Function Execution (CTFE) consente di valutare completamente funzioni complesse in fase di compilazione, indipendentemente dai livelli di ottimizzazione.
Ottimizzazioni C
Se sei un programmatore C medio, sai che il codice semplice può essere considerato affidabile per essere valutato in fase di compilazione grazie agli ottimizzatori. Ad esempio, se scrivi qualcosa del tipo:
void square(int x) { return x*x; }
void foo(void)
{
int k = square(32);
/* … */
}
ti fidi del fatto che il tuo compilatore valuti il quadrato in fase di compilazione, quando le ottimizzazioni sono attive. Quando le cose si fanno più complicate:
int factorial(int x)
{
int result = x;
while (–x)
result *= x;
return result;
}
void foo(void)
{
int k = factorial(8);
/* … */
}
I programmatori C diventano immediatamente meno sicuri di ciò che accadrà in fase di esecuzione. Ad esempio, diresti che il tuo compilatore è in grado di espandere il codice sopra in fase di compilazione o no? In realtà la risposta è “sì” in questo caso particolare (a meno che non si utilizzi un compilatore molto vecchio), ma il punto è comunque valido: questo non è codice C che si scriverebbe se si vuole essere sicuri che l’intero calcolo sia corretto. piegato in fase di compilazione.
C’è anche un altro problema: poiché il linguaggio non impone che il valore venga piegato (e in effetti, non viene piegato quando le ottimizzazioni sono disabilitate), non è possibile crearne una costante, ad esempio assegnandola a una variabile const .
Ora proviamo con una soluzione (molto ingenua e semplice) del problema n. 1 del Progetto Euler:
#include <stdio.h>
int euler1(int max)
{
int i,res=0;
for (i=1; i<max; i++)
{
if ((i % 3) == 0 || (i % 5) == 0)
res += i;
}
return res;
}
int main()
{
int r10 = euler1(10);
int r1000 = euler1(1000);
printf(“%d %d\n”, r10, r1000);
return 0;
}
Questo programma calcola semplicemente la somma di tutti i divisori di 3 o 5 inferiori a 1000. Ma se guardi il codice generato con GCC sotto -O3, vedrai che i risultati effettivi non vengono calcolati in fase di compilazione, ma piuttosto calcolati in fase di esecuzione . Credo che qualsiasi programmatore C medio sarebbe d’accordo sul fatto che non dovremmo aspettarci che questo codice venga piegato in fase di compilazione.
Ora, incontra il codice D equivalente:
int euler1(int max)
{
int i,res=0;
for (i=1; i<max; i++)
{
if ((i % 3) == 0 || (i % 5) == 0)
res += i;
}
return res;
}
int main()
{
int r10 = euler1(10);
int r1000 = euler1(1000);
printf(“%d %d\n”, r10, r1000);
return 0;
}
Già visto? Sì, è esattamente lo stesso, a parte l’istruzione include iniziale che non è richiesta (in realtà, non esiste un preprocessore in D e i moduli si riferiscono tra loro con l’istruzione import, ma printf è integrato). Naturalmente, l’esempio sopra è stato realizzato manualmente per renderlo valido sia in codice C che D, ma essendo D un’evoluzione del C, la sintassi di base è la stessa.
La varietà di colori delle uova, da rosa a blu, riflette la genetica delle galline…
Una startup californiana, Symbrosia, propone l'uso dell'alga Asparagopsis taxiformis nei mangimi per ruminanti per ridurre…
La nave rompighiaccio nucleare "Chukotka", varata a San Pietroburgo, rappresenta un passo cruciale per la…
Scoperta nel Colorado fornisce nuove prove sulla glaciazione sturtiana, rivelando che le calotte di ghiaccio…
Il progetto del Ponte sullo Stretto di Messina avanza con l'approvazione della Commissione Tecnica, introducendo…
Nel 2025, il tasso di rivalutazione del montante contributivo per le pensioni aumenterà dal 2,3%…