Archivi delle etichette: programmazione

Breve paragone tra l’“overloading” in C++, C(11) e Fortran(90)

fvc

In C++ possiamo scrivere codice come questo:

#include <iostream>
 
int miafunz(int a, int b)
{
    std::cerr << "int, int\n";
    return a + b;
}
 
int miafunz(double a, int b)
{
    std::cerr << "double, int\n";
    return 2.0*a + 2.5*b;
}
 
int main()
{
    double d0 = 1.0;
    int di = 2;
    std::cout << miafunz(d0, di) << "\n";
    std::cout << miafunz(5, di) << "\n";
}

Lo compiliamo e lo eseguiamo:

fvc-compilesegui

Quello che accade dietro le quinte è che il compilatore in realtà genera due funzioni diverse e “capisce” qual è quella giusta da chiamare basandosi sul tipo degli argomenti.

Per il programmatore è tutto trasparente, mentre compilatore e linker hanno una gatta da pelare in più. Il problema è questo: le due funzioni sono in realtà diverse, pezzi di codice diverso, e ciascuna deve avere associato un simbolo diverso1, anche se il programmatore usa solo il nome che ha deciso (miafunz).

Se non è definita e imposta un’ABI specifica per tutte le implementazioni del C++, ci possono essere “incomprensioni” perché ogni compilatore “decora” i nomi delle funzioni come meglio crede. (Cfr. name mangling)

Detto altrimenti: il compilatore C++ si deve inventare due nomi per le due funzioni, laddove noi usiamo un unico nome. È abbastanza ragionevole ipotizzare che i nuovi nomi siano costruiti a partire dal nome da noi scelto, secondo un certo schema2 — ma non è affatto necessario che sia così. Nel caso di g++ (4.7.2),

fvc-readelfgcc

ed è lo stesso con clang++ (3.0), per la cronaca. È buono che ci sia una certa convergenza, ma purtroppo in realtà non possiamo farci affidamento. Invece di spiare l’ELF potete dire al g++ di fermarsi alla generazione del codice assembly (opzione -S) e dare uno sguardo ai simboli usati.

fvc-asmsym

Il C non ha di questi problemi

Il C non ha di questo problemi perché non permette l’overloading. Se scrivete

#include <stdio.h>

int miafunz(int a, int b)
{
    fprintf(stderr, "int, int\n");
    return a + b;
}

int miafunz(double a, int b)
{
    fprintf(stderr, "double, int\n");
    return 2.0*a + 2.5*b;
}


int main(void)
{
    double d0 = 1.0;
    int di = 2;
    printf("%d\n", miafunz(d0, di));
    printf("%d\n", miafunz(5, di));
    return 0;
}

il compilatore si ribella,

fvc-gccribelle

perché stiamo ridefinendo una funzione (ho usato gcc 5.2 perché evidenzia meglio gli errori, in stile clang). In C i simboli delle funzioni devono essere unici (all’interno del loro campo di visibilità3).

Potremmo scrivere

int miafunz_ii(int a, int b)
{
    fprintf(stderr, "int, int\n");
    return a + b;
}

int miafunz_di(double a, int b)
{
    fprintf(stderr, "double, int\n");
    return 2.0*a + 2.5*b;
}

ma poi starebbe a noi scegliere la funzione “giusta” per gli argomenti giusti. Fattibile, ma potenziale fonte di errori e comunque appesantisce la lettura (e la scrittura…). Un esempio più concreto è quello di certe funzioni matematiche per le quali in effetti nella libreria esistono diverse versioni a seconda del tipo di argomenti usati e restituiti; p.es. sin (double), sinf (float), sinl (long double).

Il C11 è venuto incontro a questa esigenza tramite una macro, _Generic, il cui uso però risulta limitato, cioè non dà la stessa elasticità dell’overloading del C++: in pratica è utile solo per tgmath.h (type generic math).

Riciclo un esempio da un mio post su un altro blog.

Il Fortran è diverso

Quando le persone sentono Fortran credono di essere tornati indietro nel tempo: è un linguaggio vecchio, ormai in disuso, è brutto, ecc… Non è così. Di sicuro è “di nicchia”. Ma comunque, a differenza di altri linguaggi di quell’epoca, è andato avanti e si è ammodernato. L’ultima versione dello standard è il Fortran 2008, ma è pianificato il Fortran 2015.

Una delle caratteristiche del Fortran moderno sono le interfacce. In pratica il Fortran ha lo stesso problema che il C(11) ha in parte risolto con _Generic; però la soluzione è molto più… generica!… elastica, elegante e (a mio immodesto avviso) intelligente.

Dettagli sintattici a parte, il programmatore non lascia al compilatore l’onere di generare i nomi delle funzioni: egli scrive le diverse funzioni come deve (p.es. miafunz_di, miafunz_ii) e specifica che miafunz è un’interfaccia a quelle funzioni (il compilatore di solito è nelle condizioni di poter dedurre da sé la funzione giusta in base al tipo degli argomenti, proprio come avviene per il C++; nei casi in cui non dovesse essere così, nell’interfaccia dovremmo specificare per intero gli argomenti e i loro tipi).

Riciclo di nuovo un esempio da un altro blog; nell’esempio my_sum è definita come interfaccia per le funzioni my_sum_r, my_sum_i, ecc…


  1. A codice oggetto generato, non è sempre necessario. Per esempio nell’esempio specifico in teoria non c’è nessun bisogno che i simboli delle funzioni rimangano, perché di fatto non ci interessa esportarli. Tant’è che tali simboli possono essere rimossi, per esempio con strip. Il discorso sarebbe diverso se stessimo scrivendo del codice che deve essere linkato (una libreria statica o dinamica, per esempio).

  2. Una soluzione ragionevole (perché intellegibile) è quella di codificare nel nome della funzione i tipi degli argomenti; vediamo infatti che miafunz(double,int) diventa _Z7miafunzdi e miafunz(int,int) diventa _Z7miafunzii.

  3. O «confine entro cui sono visibili». È la mia traduzione approssimativa di scope. Non voglio affrontare questioni linguistiche sottili, ma la traduzione “visibilità” non mi sembra del tutto corretta. La visibilità (visibility) è la proprietà di essere visibili (la proprietà di un oggetto di poter essere visto); il termine scope fa riferimento a quanto è “estesa” questa proprietà, cioè fin dove vale o non vale per un determinato oggetto; dunque si riferisce all’“area di pertinenza” di una proprietà (in generale, e della visibilità nello specifico). La traduzione “visibilità” di per sé non veicola l’idea di “estensione”. Prendiamo per esempio la frase «the name must be unique in its scope». La traduzione «il nome deve essere unico nella sua visibilità» non corrisponde all’originale; in italiano siamo costretti ad usare una perifrasi: «il nome deve essere unico» «all’interno dell’ambito in cui è visibile», o «all’interno dell’estensione della sua visibilità», o «entro la portata della sua visibilità», o «nel suo campo di visibilità» (forse l’opzione migliore), o altre espressioni simili. Per quanto mi riguarda, in casi come questi non ho nessun problema ad usare termini inglesi nell’italiano settoriale.

2+2=5 in C…

Come primissimo e rapido contributo minimo a questo blog, mi aggancio a Errore! manco 2+2 sa fare per mostrarvi come lo farei in C; le prime 3 soluzioni viste su StackExchange mi sembrano insoddisfacenti.


#include <stdio.h>

#define a a=5,b

int main(void)
{
    int a = 2 + 2;
    printf("%d\n", a);
    return 0;
}

Sfrutto le macro, che secondo qualcuno andrebbero evitate il più possibile e sono pericolose: questo esempio mostra che effettivamente possono creare qualche problemino… Ma il C ci convive da sempre e io continuo a difenderle — ma sono anche tra quelli che non inorridiranno il giorno in cui avremo import <stdio.h> o qualcosa di simile.

Post scriptum

Per qualche misterioso caso c’era un erroruccio nella define… nessuno lo notò… Per fortuna su StackExchange non ho fatto lo stesso errore ;-D

Project Euler – Una piccola intro

euler_portrait

Ok, ci sono libri e tutorial, ma per imparare a programmare bisogna .. programmare! Se poi la matematica vi appassiona e le sfide vi intrigano siete nel posto giusto 😉

Project Euler è un progetto nato nel 2001 da una “costola” di matschallenge.net e divenuto un progetto indipendente nel 2006.

Qui trovate una serie di quesiti matematici di difficoltà crescente sulle quali testare la vostra abilità di programmatori. Create il vostro account e, una volta risolto correttamente un problema, avete accesso alla relativa discussione dove si commentano le soluzioni.

Attenzione però: un buon algoritmo non si limita a funzionare, ma deve essere efficiente! Se il vostro PC ha impiegato mezza giornata (e 5000 Watt) per trovare il risultato significa che potete fare di meglio, e la discussione vi aiuterà a capire dove e come è possibile ottimizzare.

Siccome la competizione è il sale delle nostre esistenze, una delle prime cose che impareremo è come ottenere il tempo di esecuzione del codice per confrontarlo con gli altri; io programmo programmicchio in Python e vi regalo subito le istruzioni che generano il report:


import time

start_time = time.time()

#qui i vostri algoritmi

elapsed_time = time.time() - start_time

print(elapsed_time)

Ah, se siete persone serie e usate Linux potete usare in alternativa il comando bash time:

time /percorso/script -opzioni argomenti

Insomma scorrendo i problemi di Project Euler prendiamo confidenza con funzioni, loop, ricorsioni, liste, istruzioni condizionali e debug, poi impariamo (o rinfreschiamo la memoria su) tante belle meraviglie matematiche. Controindicazioni? Può causarvi insonnia, sapete com’è quando ci si diverte vero?

Kinder programming

Dexter

(questo post è gentilmente offerto da piergiu, nuovo e prezioso collaboratore di ok panico!)

“Ci scusiamo per l’intrusione, il palinsesto di “Ok, panico” riprenderà al più presto!”

Questo post è rivolto ai giovani, che dico giovani, giovanissimi, anzi, appena usciti dall’asilo!
Voi direte: ” Eh, adesso cosa vuoi dire, che questo post di ok panico è per i bambini di 6 anni? eh? vuoi insegnare loro a programmare in python o c++? già da questa età?”
e io potrei tranquillamente rispondervi: “Insegnare a programmare ai bambini di 6 anni? Non io, ma voi! e non in python o c++, troppo presto..casomai tornano qui su Ok, panico e sfogliano il grande archivio costruito da juhan, dr.prof e altri!”

Non tanto per tentare inutilmente di fare concorrenza all’India che sforna prodigi informatici di giovane età,  ma per far si che i nostri futuri successori siano già pronti e con delle armi in più per affrontare questo mondo che ogni giorno trova modo di programmarci! (“ah ci metti pure la morale eh?” – “no però controlla questo video (link a youtube) e poi mi dirai..”)

Quanto iniziai sbadatamente a programmare fu in prima superiore col pascal e turbopascal: un pochetto ostico tant’è che solo in pochi di quella classe di una 20ina studenti ci ‘affezionammo’ alla programmazione. Peccato!

Forse era anche già tardi, ma chissà: per questo non perdiamo altro tempo, bando alle ciance e iniziamo sul serio.
Infondiamo il piacere del programmare ai giovincelli!

Scratch!

Non si tratta di un linguaggio di programmazione, ma il concetto è quello. Certo a un bambino non interessa (per ora) se questo linguaggio sia o meno Turing completo. Non interessa nemmeno avere subito a che fare con i flowchart.
Diamogli qualcosa di familiare, come dei blocchi,mattoncini..mattoncini stile lego!
Andate qua al sito ufficiale e prelevate una bella copia di questo software: anche se siete esperti, vi farà piacere usare questo software.

Scratch è sviluppato dal Lifelong Kindergarten Group dei Media Lab del MIT, con il supporto finanziario della National Science Foundation, di Microsoft, Intel Foundation, Nokia, Iomega, e dei consorzi di ricerca dei Media Lab del MIT.

Grandi nomi per un progetto ambizioso(non sono solo io con idee strane, di programmatori giovani alla Dexter ..)

Quando i ragazzi creano e condividono i loro progetti Scratch, imparano importanti idee matematiche e computazionali e allo stesso tempo imparano a pensare creativamente, a ragionare con sistematicità e a lavorare in collaborazione.

La comunità online di Scratch ne fa di tutti i tipi, dai giochi alle avventure grafiche, ma anche la semplice programmazione è ammessa.
Infatti esistono varie sezioni di ‘mattoncini’ o blocchi, dove è possibile inizializzare variabili, cambiarne valore, generare numeri random, fare confronti da risultati booleani, for, while, while(true), do-while e altri, come anche controlli di collisione e gestione di varie tipologie di eventi: insomma Scratch è per l’informatica, quanto un Phun( ora Algodoo ) è per la fisica, o Geogebra per algebra/matematica!

L’interfaccia è uber-semplice, e l’uso ancora di più: basta fare drag ‘n’drop dalla palette dei blocchi e incastrarli nel piano di lavoro, nel modo più oppurtuno, creando sequenze con annidamenti e gestione parallela di eventi!
Un esempio: ho creato questo script/sketch/scratch che di solito faccio con la penna su un foglio quadrettato quando sono in una situazione noiosa(meeting/lezione/{a scelta}) e non posso uscirne immediatamente.

Da un quadretto mi sposto seguendo una direzione e riempiendo i quadretti che incontro fino ad arrivare ad un bordo per poi cambiare direzione. Fino a riempire il foglio, o fino a creare un qualche pattern carino, o fino a fine inchiostro.
State già pensando a come farlo in Java, c++, python, visualbasic, in fortan vero? Nerd-power ♥
(fatemi sapere come avete fatto voi qui sotto con un commento, ci tengo!)

Ebbene ecco uno screenshot del programma creato in due minuti circa.


Lo scopo originale di Scratch è quello di impartire programmi a un esserino dalle sembianze di un gatto, ma in realtà io ho cambiato semplicemente la cosidetta “sprite”, immagine, che rappresenta il gatto e l’ho sostituita con un pixel(tutto nello stesso ambiente di Scratch).

Per ogni direzione impostata si creano pattern differenti.
Come si può notare ho creato due blocchi, uno che risponde all’evento del click sulla bandiera verde per mandare in Run lo script e uno che risponde alla pressione della barra spaziatrice per fermare lo script.
Nel primo inizializzo delle variabili che mi servono come range per il punto iniziale del disegno, scelto random.
Poi passo a disegnare e in una sorta di loop infinito eseguo i movimenti e controllo se è necessario cambiare direzione poiché giunti al bordo.
Nel secondo blocco impongo di fermare tutta l’esecuzione alla pressione della barra spaziatrice.

Notare il linguaggio naturale, dall’uso dell’imperativo nei verbi, come a ricordare cosa è un algoritmo, ovvero una sequenza di passi da fare per raggiungere uno scopo.
Non c’è molto altro da dire a riguardo, la classica situazione del “si fa prima a fare che a dire”.
Possibilità di playback di audio e di uso di penna( come nell’esempio ), come anche possibilità di mettere in pausa un processo per {n} secondi e così via.

Concludendo questa invasione barbarica su Ok, Panico, vi invito a provare voi stessi Scratch e sopratutto ai vostri parenti più piccoli: evangelizzateli con la parola dell’informatica. Fatemi sapere come va!
Ramen.

Se siete arrivati a leggere fin qua, vi ringrazio per la pazienza e vi ricordo :

Programma o sii programmato.