Un nuovo dispositivo per la gestione del dolore

Oggi vorrei condividere con tutti voi un articolo, a mio parere molto interessante.
Parla della terapia del dolore. Un argomento delicato e importante.
Dei ricercatori hanno inventato un impianto elettronico flessibile che potrebbe, un giorno, rendere la gestione del dolore molto più “fredda” .

Creato con materiali che si dissolvono nel corpo, il dispositivo circonda i nervi con un dispositivo di raffreddamento evaporativo. Impiantato nei ratti, il dispositivo di raffreddamento ha impedito ai segnali di dolore di arrivare al cervello, come riferiscono il bioingegnere John Rogers e colleghi su Science del 1 luglio.

Sebbene non sia pronta per l’uso umano, una versione futura potrebbe potenzialmente consentire ai “pazienti di aumentare o diminuire il sollievo dal dolore di cui hanno bisogno in un dato momento”, afferma Rogers, della Northwestern University di Evanston, Ill.
Gli scienziati sapevano già che le basse temperature possono intorpidire i nervi del corpo. Pensa alle dita congelate in inverno, dice Rogers. Ma imitare questo fenomeno con un impianto elettronico non è facile. I nervi sono fragili, quindi gli scienziati hanno bisogno di qualcosa che abbracci delicatamente i tessuti. E un impianto ideale verrebbe assorbito dal corpo, quindi i medici non dovrebbero rimuoverlo.

Realizzato con materiali solubili in acqua, il dispositivo del team è dotato di un polsino morbido che avvolge un nervo come la carta igienica su un rotolo. Piccoli canali serpeggiano lungo la sua lunghezza gommosa. Quando il liquido di raffreddamento pompato attraverso i canali evapora, il processo assorbe calore dal nervo sottostante. Un sensore di temperatura aiuta gli scienziati a raggiungere il punto debole: abbastanza freddo da bloccare il dolore ma non troppo freddo da danneggiare il nervo.

Ora i ricercatori vogliono esplorare per quanto tempo possono applicare l’effetto di raffreddamento senza danneggiare i tessuti, dice Rogers. Quanto potrebbe aiutare i malati che soffrono..


 

Supportaci condividendo sui social il nostro articolo!

L’inventrice della lavastoviglie

Josephine Garis Cochrane era una donna indipendente della metà del 1800. Quando ha sposato il marito William Cochran, ha preso il suo cognome ma ha aggiunto una “e” alla fine. E quando si è resa conto che nessuno aveva ancora creato una vera lavastoviglie automatica, ne ha inventata una lei stessa!

Josephine condusse una vita agiata a Shelbyville, nell’Illinois. William era un uomo d’affari di successo e la coppia organizzava spesso cene nella loro grande casa. Aveva anche servitori da pulire in seguito. Ma una ma

 

ttina, dopo una festa, scoprì che alcune delle sue porcellane erano scheggiate. Era così sconvolta che da quel momento in poi decise di lavare i piatti da sola. Non passò molto tempo prima che Josephine si chiedesse perché nessuno avesse inventato una macchina per fare il lavoro… e presto aveva abbozzato l’idea che sarebbe diventata la prima lavastoviglie automatica di successo commerciale.

Il suo design utilizzava la pressione dell’acqua per pulire, proprio come fanno le lavastoviglie di oggi. Aveva scomparti di filo per le stoviglie, che si inserivano in una ruota all’interno di una caldaia di rame. Un motore faceva girare la ruota mentre acqua saponosa spruzzava sui piatti. Era pratico, ma Josephine ha avuto difficoltà a cercare di assumere un meccanico per costruire la sua macchina come voleva, invece di insistere nel costruirla a SUO modo. Alla fine trovò un uomo di nome George Butters con cui lavorare e la macchina per lavare i piatti Garis-Cochran fu brevettata nel 1886, tre anni dopo la morte del marito.

Josephine pensava che la sua invenzione sarebbe stata apprezzata da altre casalinghe, ma ha avuto più successo in hotel e ristoranti, forse perché era un elettrodomestico costoso da acquistare per una famiglia normale. Aprì la sua fabbrica nel 1897 e vendette personalmente le sue macchine quasi fino alla sua morte nel 1913. Nel 1926, la sua azienda fu acquistata da Hobart, che alla fine divenne il gigante degli elettrodomestici KitchenAid.
Io la farei subito santa…

Supportaci condividendo sui social il nostro articolo!

Semiconduttori bidimensionali

I semiconduttori bidimensionali (2D) sono una classe di materiali semiconduttori con spessori su scala atomica. Questi materiali hanno numerose proprietà vantaggiose, tra cui una buona mobilità a spessori inferiori a 1 nm, che li rendono particolarmente promettenti per lo sviluppo di transistor ad effetto di campo (FET) e altri componenti elettronici, fotonici e optoelettronici.
Nonostante i loro vantaggi, quando vengono utilizzati per costruire componenti elettronici, questi materiali spesso presentano una stabilità elettrica limitata. La ragione principale di ciò è che i portatori di carica provenienti dai semiconduttori possono con difetti negli isolanti che interagire con i materiali all’interno dei dispositivi, ostacolando la stabilità dei dispositivi.

I ricercatori di TU Wien AMO GmbH hanno recentemente dimostrato una che potrebbe essere utilizzata per migliorare la stabilità dei FET e della

strategia materiali 2D. Questa strategia introdotta in un articolo pubblicato su Nature Electronics, comporta un punto del livello di Fermi dei materiali 2D, assicurando che massimizzi la distanza di energia tra i portatori di messaggero e i difetti nell’isolatore di gate mentre il dispositivo è in funzione .

Inoltre, la stessa strategia potrebbe essere applicabile a un’ampia gamma di isolanti, compresi gli isolanti cristallini. Nei loro prossimi studi, i ricercatori intendono testare ulteriormente la strategia, per i livelli di stabilità che potrebbe consentire con diverse combinazioni di proposta materiali.

Supportaci condividendo sui social il nostro articolo!

La Russia e la Stazione Spaziale Internazionale

50 anni fa, gli Stati Uniti e l’Unione Sovietica unirono le forze per la scienza

Così riporta un articolo del 3 giugno 1972 di Science News

I leader statunitensi e sovietici … hanno firmato accordi su spazio, scienza e tecnologia, salute e ambiente …. L’accordo spaziale… delinea piani di cooperazione in campi come la meteorologia, lo studio dell’ambiente naturale, l’esplorazione planetaria e la biologia spaziale.

 

L’accordo spaziale del 1972 portò al primo volo spaziale umano internazionale, la missione Apollo-Soyuz, durante la quale gli equipaggi sovietici e statunitensi socializzarono nello spazio. Apollo-Soyuz ha incoraggiato decenni di collaborazione che continua ancora oggi sulla Stazione Spaziale Internazionale. Ora, la guerra della Russia in Ucraina ha spinto molti paesi a ritirarsi dagli sforzi scientifici con la Russia, nello spazio e sulla Terra. Mentre la NASA rimane impegnata con la stazione spaziale, il capo dell’agenzia spaziale russa ha minacciato di porre fine alla cooperazione per rappresaglia per le sanzioni imposte in risposta alla guerra. La Russia deve ancora abbandonare la stazione, sebbene il Paese abbia smesso di fornire motori a razzo agli Stati Uniti.

Supportaci condividendo sui social il nostro articolo!

Stiamo andando a fuoco…

Stiamo andando a fuoco, letteralmente e, quindi ho fatto una riflessione riguardante gli incendi che stanno devastando il mondo. Ho trovato un interessante articolo. L’articolo riporta che pezzi di carbone sepolti in antiche rocce rinvenute in Galles e Polonia sono le prove di incendi avvenuti circa 430 milioni di anni fa. Oltre a battere il record precedente di circa 10 milioni di anni, i reperti aiutano a stabilire la quantità di ossigeno presente nell’atmosfera terrestre in quel momento. L’antica atmosfera doveva contenere almeno il 16% di ossigeno, secondo quanto riportato dai ricercatori il 13 giugno su Geology. Questa conclusione si basa sui test di laboratorio moderni che mostrano la quantità di ossigeno necessaria affinchè un incendio prenda piede e si diffonda. Mentre l’ossigeno costituisce oggi il 21% della nostra aria, negli ultimi 600 milioni di anni circa, i livelli di ossigeno nell’atmosfera terrestre hanno oscillato tra il 13% e il 30%. Il carbone, residuo di un incendio, è una prova fisica che fornisce, almeno, una soglia minima per le concentrazioni di ossigeno. Questo perchè l’ossigeno è uno dei tre ingredienti necessari per creare un incendio. Il secondo, l’accensione, è avvenuto mediante un fulmine. Il terzo, il combustibile, proveniva da piante. La vegetazione predominante era costituita da piante a bassa crescita, alte solo un paio di centimetri. E’ vero che i livelli di ossigeno stanno cambiando ma è pur vero che l’uomo sta facendo di tutto affinchè avvenga troppo repentinamente…

Supportaci condividendo sui social il nostro articolo!

Il terminale, la console e il linguaggio C

Lo scopo dell’istruzione è quello di trasformare gli specchi in finestre.

Sydney J. Harris

La console testuale, la mitica “finestra nera” per i non addetti ai lavori, è da sempre croce e delizia per noi sviluppatori software. Nella sua apparente semplicità, tale ambiente si mostra amichevole o perfido a seconda della conoscenza che noi abbiamo di lui, della sua struttura interna e dei comandi che accetta. Ho usato volutamente il pronome personale “lui” in quanto noi informatici siamo soliti attribuire una personalità agli oggetti con i quali sistematicamente interagiamo nella nostra vita quotidiana.

Ogni sistema software ha da sempre una interfaccia a caratteri, la cosiddetta shell testuale, che ci permette di interagire con il sistema sottostante: nel mondo Unix-Linux essa prende il nome di terminale mentre nel contesto Windows è chiamata più spesso console o anche cmd.exe, dal nome del file eseguibile che la manda in esecuzione.

In questo breve viaggio cercherò di mostrare come interagire, usando il linguaggio C, con la console in questione per realizzare delle semplici operazioni di spostamento del cursore da un punto ad un altro della finestra. Infatti, una delle difficoltà maggiori che abbiamo nella gestione di tale finestra è la sua assoluta rigidità dovuta al fatto che il prompt dei comandi è apparentemente immutabile nella sua acquisizione di un comando da tastiera e nella relativa restituzione dell’output del comando stresso. Se questa situazione è del tutto scontata e naturale in un contesto classico di gestione testuale lo stesso non vale nel momento in cui vogliamo gestire in maniera un po’ più “grafica” la nostra console.  Mi riferisco qui alla possibilità di realizzare un minimo di interattività all’interno della finestra muovendo, ad esempio, il cursore a piacimento in una specifica posizione al fine di simulare un classico ambiente a menu piuttosto che un rudimentale videogioco pseudo-grafico.

Il terminale e il mondo Unix-like

Nel mondo cosiddetto Unix-like, ovvero nel contesto di derivazione Unix quale Linux e macOS, il nostro terminale può essere gestito al meglio facendo uso della libreria di funzioni ncurses il cui sito ufficiale è:

https://invisible-island.net/ncurses/

Ovviamente ci si dovrà accertare della disponibilità della librerie in questione. A solo titolo di esempio, l’installazione in un sistema Debian-Ubuntu la si realizza con il gestore di pacchetti apt nel seguente modo:

sudo apt-get install libncurses5-dev libncursesw5-dev

Una volta risolti i problemi di installazione si potrà editare il proprio file sorgente con un editor di propria scelta. Io uso per lo più l’editor nano e, solo quando costretto dalle circostanze, il mitico vim.

Un “salve mondo” con ncurses, ovvero il tipico primo programma che si realizza per un qualsiasi ambiente di programmazione potrebbe essere il seguente:

 

#include <ncurses.h>

int main()

{                                                                                                                     

   initscr();

   printw("Salve mondo da ncurses!");

   refresh();

   getch();

   endwin();

   return 0;

}

 

Tale semplicissimo snippet (pezzo di codice sorgente) dovrebbe far comprendere almeno in linea generale la modalità di utilizzo della libreria in questione.

La prima cosa che faccio notare è il fatto che ho inserito il solo include della libreria ncurses.h in quanto la sola riga:

#include <ncurses.h>

Include in automatico altre librerie tra cui anche stdio.h.

La prima istruzione che incontriamo, la chiamata alla funzione initscr() inizializza l’ambiente ncurses, ambiente che viene poi chiuso con la successiva endwin(). Faccio notare che l’istruzione initscr()  non effettua la cancellazione dello schermo ma predispone tutta una serie di strutture dati  per interfacciare ncirses con l’hardware video del sistema su cu è in  esecuzione.

Di seguito incontriamo l’istruzione:

 

printw("Salve mondo da ncurses!");

 

che, senza sorprese predispone la stampa di una stringa di saluto. In realtà, la scrittura avviene preventivamente in uno specifico buffer che verrà poi dirottato in output grazie alla funzione refresh() che aggiornerà il nostro display video. Ci sono poi un altro paio di cose che vale la pena di sottolineare. La prima riguarda l’assoluta necessità di usare l’istruzione getch() per consentire la visualizzazione dell’output a video in quanto, in caso contrario, scomparirebbe immediatamente alla vista riportando la sola visualizzazione del prompt dei comandi. La seconda cosa riguarda il fatto che la funzione getch() effettua una sorta di aggiornamento  implicito e automatico dello schermo e che quindi, teoricamente, in questo caso specifico di esempio minimale anche senza la chiamata della funzione refresh, l’output a video verrebbe comunque mostrato.

Per la compilazione useremo il seguente comando:

 

gcc  mycurse.c -lncurses

 

Attenzione alla necessità di compilare usando l’opzione -lncurses in quanto ncurses non è semplicemente un file di intestazione ma una vera e propria libreria e  quindi -l ne impone il linking. Ovviamente, per lancoare il nostro eseguibile digiteremo il classico ./a.out.

Al solo scopo di far comprendere la semplicità d’uso della libreria vi mostro ora una funzione che consente di spostare il punto di editing, il nostro cursore, in una specifica posizione:

 

move(y,x);

 

dove y è il valore per la riga e quindi, partendo dall’angolo in alto a sinistra, che ha coordinata (0, 0), aumenta verso il basso mentre x è il valore per la colonna che quindi, banalmente, aumenta da sinistra verso destra.

Un’altra funzione semplice e interessante è:

 

mvprintw(y,x,formato,argomenti[...])

 

che consente di stampare una data stringa in una qualsiasi posizione del nostro terminale.

Questo brevissima panoramica sulla libreria ncurses voleva solo sollecitare l’interesse per un sistema software che, se be padroneggiato può dare grandi soddisfazioni come dimostra plasticamente la figura relativa al videogioco Rouge sviluppato appunto sfruttando tale libreria e che ha segnato un’epoca guadagnandosi un posto nella storia dei videogame.

Figura 1 Il videogioco Rouge realizzato con le librerie  ncurses.

 

 

 

 

 

 

 

 

 

Cmd.exe e la console testuale come non l’hai mai vista prima

La libreria ncurses è sicuramente uno strumento formidabile relativo al mondo Unix-like. Tuttavia, può essere interessante capire come gestire la console a caratteri in maniera evoluta anche sul sistema di zio Bill. Per farlo, ovviamente, possiamo sfrutta il WSL,  ovvero il Sottosistema Windows per Linux all’interno di Windows 10, così come usarlo in ambiente di emulazione Cygwin. Volendo potremmo addirittura sfruttare sotto Windows le librerie ncurses con il linguaggio di programmazione Python. Ma a noi piacciono le cose complesse e ardite per cui di seguito vi mostro come realizzare “a mano” qualcosa di simile alle ncurses direttamente sotto Windows. Pronti? Partiamo!

Sappiamo bene che la console cmd.exe è in effetti estremamente rigida ma cercheremo ora di forzarla a essere più duttile rispetto alle nostre necessità e di renderla, in un certo qual modo, “grafica”. Ovviamente, la prima cosa che dobbiamo cercare di fare è quella di poter scrivere un certo simbolo in una posizione qualsiasi della console e non quindi semplicemente sul nostro prompt dei comandi. Questa operazione, non propriamente banale, è possibile grazie alla libreria windows.h ed alla funzione SetConsoleCursorPosition che consente appunto di settare (collocare) il cursore in una data posizione della console. Per rendere il tutto più immediatamente comprensibile vi mostro subito un pezzo di codice minimale che realizza quanto detto.

 

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

void gotoxy(short x, short y);

int main()
{

    gotoxy(10,1);

    printf("X");

     getchar();

     return 0;

}



void gotoxy(short x, short y)
{
    COORD pos ={x,y};

    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}

 

 

Analizziamo quindi il codice precedente, osservando innanzitutto, come già anticipato, la necessità di includere la libreria windows.h. Subito dopo troviamo l’intestazione di una funzione gotoxy che abbiamo scritto con lo specifico scopo di spostare il nostro cursore in una determinata posizione che passeremo come argomento alla funzione stessa. La funzione in questione richiama quindi  al suo interno SetConsoleCursorPosition. Quest’ultima prende in input il riferimento alla finestra in uso e le coordinate  per la nuova posizione del cursore. Per ulteriori dettagli su tale funzione è possibile rifarsi al link:

 

https://docs.microsoft.com/it-it/windows/console/setconsolecursorposition

 

Qui si può facilmente scoprire che le coordinate da passare alla funzione sono la colonna e la riga di una cella del buffer dello schermo. Ovviamente, le coordinate in questione devono trovarsi all’interno dei limiti del buffer dello schermo della console. Nel caso in cui la funzione ha esito negativo verrà restituito il valore zero. Al momento, per una questione di semplicità non effettuiamo uno specifico controllo di errore che comunque dovrebbe essere sempre fatto con l’ausilio della funzione GetLastError.

 

Figura 2 – La console dopo lo spostamento del cursore

 

 

 

 

 

 

 

Come si intuisce, la coordinata 0, 0 è nell’angolo in  alto a sinistra e quindi è come se ci trovassimo in un piano cartesiano con x che cresce verso sinistra e y che cresce verso il basso.

Per rendere immediatamente interessante quello che stiamo facendo, possiamo immaginare di far muovere il nostro cursore sulla console usando i mitici e classici tasti freccia. Per farlo dobbiamo innanzitutto poter leggere i tasti della tastiera e quindi solo dopo spostare il cursore in maniera consistente. Per leggere la tastiera possiamo sfruttare le funzioni kbhit e getch.

È importante precisare che entrambe le funzioni in questione non sono il massimo della standardizzazione e che quindi devono essere usate consapevoli del fatto che sarà necessario verificare gli eventuali vincoli delle specifiche piattaforme. In ogni caso, kbhit legge la tastiera e restituisce un valore diverso da zero in caso di pressione di un tasto mentre getch restituisce il valore corrispondente al tasto digitato. Vediamo allora il codice che ci permette di leggere la digitazione dei vari tasti e interrompere l’esecuzione in caso di digitazione del tasto ESC, escape, che corrisponde al valore 27.

 

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>

int main()
{
    printf("Premere ESC per interrompere.\n");

    int ch;

    while (TRUE)

    {

          if ( kbhit() )

          {

            //

            ch = getch();

            if ((ch == 27))

            {

            break;

             }

            printf("Tasto: %c numero: %d\n", ch, ch);

        }

    }

   
    printf("Programma terminato!");

    getchar();

    return 0;

}

 

Il programmino in questione presenta, nel più classico dei modi,  il main loop, ovvero il ciclo principale che si imposta su pressoché qualsiasi  software e che consente di iterare le varie operazioni fin quando non se ne richiede in maniera forzata l’uscita. Il codice stesso dovrebbe essere di immediata comprensione: il ciclo itera teoricamente all’infinito (while (TRUE)) e terminerà con un break non momento in cui il tasto digitato è appunto ESC.

 

Da notare che ho usato la funzione getch e non getchar in modo da non dover attendere la pressione del tasto Invio per confermare la digitazione del singolo tasto.

 

Figura 3- Il programma per la cattura dei valori dei tasti.

 

 

Muoviamo il cursore nella console

A questo punto, maturate le competenze per spostare in una certa posizione il nostro cursore e capito come possiamo intercettare i tasti, possiamo immaginare di muovere il nostro cursore nel punto in cui vogliamo tramite specifici tasti. Per semplificare il codice evitiamo di usare i tasti freccia. Infatti, la pressione dei tasti freccia non è particolarmente standard e tra l’altro restituisce 2 differenti valori piuttosto che uno solo, così come per gli altri tasti normali. In ogni caso, non è un gran problema in quanto possiamo immaginare di usare, al posto dei tasti freccia, una combinazione classica nota come WASD. Si tratta dei tasti che hanno appunto queste quattro lettere e che vengono usate nei giochi per sostituire le frecce stesse in quanto questi hanno una organizzazione a T rovesciata del tutto simile.

 

 

Figura 4 – Il confronto tra i tasti freccia  e la combinazione WASD

 

 

 

Come si intuisce anche dalla figura, i tasti A e D corrispondono ad avanti e indietro mentre W e S ai tasti sopra e sotto. Vediamo allora come leggere questi tasti e spostare il cursore in logica  conseguenza.

Servendoci del precedente codice scopriamo anche immediatamente quali sono i valori corrispondenti ai tasti di nostro interesse e che vi riporto in figura.

 

Figura 5 – I codici dei tasti della combinazione WASD.

 

 

 

 

 

 

 

 

 

Per evitare problemi dovremo ovviamente intercettare sia il codice per le lettere minuscole sia quelle per le maiuscole. Di seguito vi riporto una possibile e semplice implementazione di quanto ci siamo proposti di fare.

 

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>


#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4

#define MAX_X 80
#define MAX_Y 25

void gotoxy(short x, short y);

short pos_x, pos_y;

int main()
{
   short pos_x=0;

   short pos_y=0;
   
   gotoxy(0, MAX_Y+1);
   printf("Premere ESC per interrompere.\n");
   gotoxy(0,0);

   int ch;

   while (TRUE)
   {

   if ( kbhit() )
   {
   ch = getch();

   if ((ch == 27))
   {
       break;
   }

   else
   {

   switch(read_key(ch))
   { 
   case UP:

   pos_y--;

   break;

   case DOWN:

   pos_y++;

   break;

   case LEFT:

   pos_x--;

   break;

   case RIGHT:

   pos_x++;

   break;

   }

   //evito che valori diventino negativi o superiori al max consentito
   if (pos_x <0) pos_x=0;
   if (pos_y <0) pos_y=0;

   if (pos_x > MAX_X) pos_x=MAX_X;
   if (pos_y > MAX_Y) pos_y=MAX_Y;

   gotoxy(pos_x, pos_y);

   printf("X");

   }




   }

   }




   printf("Game over ;) ");

   getchar();

   return 0;

}




//

int read_key(int ch)

{

int direction=0;

if (ch==119 || ch == 87) //tasto W - sopra

{

direction=UP;

}

else if (ch==97 || ch == 65) //tasto A - sinistra

{

direction=LEFT;

}

else if (ch==115 || ch == 83) //tasto S - sotto

{

direction=DOWN;

}

else if (ch==100 || ch == 68) //tasto D - destra

{

direction=RIGHT;

}

return direction;

}


void gotoxy(short x, short y)
{
COORD pos ={x,y};
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}


Cercherò ora di commentare gli elementi più salienti di quanto proposto. Innanzitutto, definiamo alcune costanti utile per semplificare la leggibilità del codice stesso, iniziando con quelle utili a definire la direzione per i nostri tasti.

 

#define UP 1

#define DOWN 2

#define LEFT 3

#define RIGHT 4

 

Successivamente definiamo il limiti del nostro campo di gioco con le seguenti:

 

#define MAX_X 80
#define MAX_Y 25

 

Ovvero, in questo caso, 25 righe per 80 colonne. Per gestire la posizione corrente usiamo, invece, le seguenti variabili globali:

 

short pos_x, pos_y;

 

Detto questo, il codice è abbastanza semplice da interpretare considerando quanto già visto in precedenza. Cicliamo, dunque, fino alla pressione del tasto escape e con il seguente switch effettuiamo, modificando le variabili pos_x e pos_y  il movimento corrispondente alla scelta effettuata:

 

switch(read_key(ch))
{
   case UP:
      pos_y--;
      break;

    case DOWN:
         pos_y++;
         break;
 
    case LEFT:
        pos_x--;
        break;

    case RIGHT:
    pos_x++;
    break;
}

Come si può facilmente comprende abbiamo creato una specifica funzione read_key per capire in quale direzione andare in quanto la funzione in questione legge i codici dei tasti e restituisce la costante della direzione da prendere.

Infine, vale forse la pena di notare le righe di codice:

 

gotoxy(0, MAX_Y+1);
printf("Premere ESC per interrompere.\n");
gotoxy(0,0);

 

che rappresentano, a livello minimale, un modo per rendere l’interfaccia più pulita spostando le istruzioni per l’uscita dal gioco in una zona esterna al nostro campo di azione.

Ovviamente, il tutto è assolutamente embrionale e vuole solo dare indicazioni di massima su come organizzare un tipico gioco. Possiamo infatti immaginare, a solo titolo di esempio, come potremmo generare in una posizione casuale del campo di gioco un qualsiasi elemento e poi far raggiungere al nostro giocatore quella posizione, ad esempio, calcolando quanto tempo ci mette per raggiungerla e restituire un punteggio in base alla sua velocità di azione. Come al solito, l’unico limite è la fantasia!

 

Carlo A. Mazzone

 

Supportaci condividendo sui social il nostro articolo!

La struttura dati Union del C: a cosa serve realmente?

“Non nei numeri ma nell’unità sta la nostra grande forza.”

Thomas Paine

 

La struttura dati union del linguaggio C è di sicuro particolare e il suo  reale e concreto utilizzo può essere a volte sfuggente. Come dovremmo sapere, una union è del tutto simile ad una struct con l’unica  apparente differenza che di una union possiamo usare, in un dato momento, uno ed uno solo dei suoi membri. Infatti, l’occupazione di spazio di una union è relativa al massimo dello spazio occupato dal più grande dei suoi membri. Per averne una prova facciamo un esempio supponendo di avere due strutture dati: una struct e una union aventi entrambe tre membri di cui un intero, un float e un double:

#include <stdio.h>
#include <stdlib.h>

struct s
{
   int i;
   float f;
   double d;
};

union u
{
   int i;
   float f;
   double d;
};


int main(int argc, char *argv[])
{
   struct s myStruct;
   union u myUnion;

   printf("\nIntero byte: %d", sizeof(myStruct.i));
   printf("\nFloat byte: %d", sizeof(myStruct.f));
   printf("\nDouble byte: %d", sizeof(myStruct.d));

   printf("\nLa struct occupa %d byte.", sizeof(myStruct));
   printf("\nLa union occupa %d byte.", sizeof(myUnion));

   return 0;
}

 

In relazione al nostro test, prima stampiamo l’occupazione in byte dei singoli membri e poi l’occupazione delle singole strutture dati. Otterremo quanto presentato in Figura.

Struct union

 

 

 

 

 

 

Figura: L’occupazione in byte di struct e union.

Come si evince da una immediata osservazione, la struct occupa ben 16 byte sommando, infatti, i 4 dell’intero, i 4 del float e gli 8 byte del double. Al contrario, la union occupa solo 8 byte, ovvero la dimensione del suo membro massimo, il double.

Se questo è polimorfismo

Resta comunque la domanda di fondo: ma a cosa serve una union? Ebbene, il suo scopo è di norma quello di gestire in un’unica struttura dati valori di tipo differente che riguardano una stessa variabile. In un certo senso è un tentativo del C di essere polimorfo (cosa che il C++ fa in maniera assolutamente naturale). In somma sintesi, per polimorfismo si intende la possibilità di gestire con una stessa variabile differenti tipologia di dato (un intero, un float, …) a seconda delle circostanze di nostro interesse. Vediamo allora il seguente codice in cui immaginiamo una union per gestire una sorta di variant ovvero una variabili che può assumere differenti valori a seconda di una specifica assegnazione:

 

#include <stdio.h>
#include <stdlib.h>

enum Type {INTEGER, FLOATS, DOUBLE};

union u
{
   int i;
   float f;
   double d;
};

void stampa(union u *, int);

int main(int argc, char *argv[])
{
   union u myVar;
   int v_type; //il tipo attualmente utilizzato

   v_type = INTEGER;
   myVar.i = 12.66;
   stampa(&myVar, v_type);

   v_type = FLOATS;
   myVar.f = 12.66;
   stampa(&myVar, v_type);

   v_type = DOUBLE;
   myVar.d = 12.66;
   stampa(&myVar, v_type);
  
   return 0;
}

void stampa(union u *u, int v_type)
{
   switch(v_type)
   {
      case INTEGER:
      printf("Valore intero: %d\n", u->i);
      break;

      case FLOATS:
      printf("Valore float: %f\n", u->f);
      break;

      case DOUBLE:
      printf("Valore double: %lf\n", u->d);
      break;
   }
}

 

Vediamo allora di interpretare il codice in questione. Innanzitutto, definiamo la nostra union con 3 differenti membri: un intero, un float e un double.

 

union u
{
   int i;
   float f;
   double d;
};

 

Per gestire al meglio le tre differenti situazione utilizziamo una enumerazione:

 

enum Type {INTEGER, FLOATS, DOUBLE};

 

mentre per controllare quale tipo di dato andiamo di volta in volta ad utilizzare usiamo la variabile

 

int v_type;

 

Infine, per simulare un caso reale di utilizzo sfruttiamo uno switch all’interno di una funzione di stampa:

 

switch(v_type)
{
   case INTEGER:
   printf("Valore intero: %d\n", u->i);
   break;

   case FLOATS:
   printf("Valore float: %f\n", u->f);
   break;

   case DOUBLE:
   printf("Valore double: %lf\n", u->d);
   break;
}

 

Struct e union: quando l’unione fa la forza

In alcune situazioni è possibile preferire un approccio in cui l’informazione relativa al tipo di dato che vogliamo utilizzare è direttamente e strettamente collegato al dato stesso. In tali contesti è possibile integrare una union all’interno di una struct così come vi mostro di seguito:

 

#include <stdio.h>
#include <stdlib.h>

enum Type {INTEGER, FLOATS, DOUBLE};

struct mydata
{
   int which_one;
   union _value
   {
      int i;
      float f;
      double d;
   } value;
};

void stampa(struct mydata *);

int main(int argc, char *argv[])
{
   struct mydata x;

   x.value.i =12;
   x.which_one = INTEGER;
   stampa(&x);

   x.value.f =10.66;
   x.which_one = FLOATS;
   stampa(&x);

   return 0;
}


void stampa(struct mydata *x)
{
   switch(x->which_one)
   {
      case INTEGER:
      printf("Valore intero: %d\n", x->value.i);
      break;

      case FLOATS:
      printf("Valore float: %f\n", x->value.f);
      break;

      case DOUBLE:
      printf("Valore double: %lf\n", x->value.d);
      break;
   }
}

 

Come si può scoprire dopo un primo attento esame, il punto nodale della nostra organizzazione è data dalla struct seguente:

 

struct mydata
{
   int which_one;
   union _value
   {
      int i;
      float f;
      double d;
   } value;
};

 

La struct in questione ha innanzitutto il membro which_one che serve per tener traccia del tipo di variabile che vogliamo gestire. Anche in questo caso, così come nell’esempio precedente, usiamo la seguente enumerazione per tracciare in modo semplice il tipo di dati utilizzato in un dato momento:

 

enum Type {INTEGER, FLOATS, DOUBLE};

 

All’interno della struct abbiamo invece la nostra union con i vari tipi di variabili:

 

union _value
{
   int i;
   float f;
   double d;
} value;

 

Per il resto il codice dovrebbe essere di relativa semplice interpretazione.

 

Carlo A. Mazzone

Supportaci condividendo sui social il nostro articolo!

Riflessioni e proposte per un nuovo Umanesimo Digitale

Carlo Mazzone

Siamo nel pieno di una rivoluzione tecnologica guidata dalle innovazioni basate sul digitale di cui è difficile definire con precisione i frastagliati contorni. Se da un lato cogliamo con favore i frutti di queste nuove tecnologie, nella forma di strumentazioni sempre più evolute quali TV, computer e cellulari, facciamo fatica a gestire le loro ripercussioni nel contesto che genericamente possiamo definire “sociale”.

In effetti, le nuove tecnologie stanno comportando mutamenti epocali sotto innumerevoli punti di vista che vanno, solo a titolo di esempio, dalle modalità di produzione e consumo dell’informazione, alla creazione di nuovi posti di lavoro, per arrivare alla necessità di rendere queste tecnologie democratiche affinché esse possano essere di beneficio per la maggioranza assoluta della popolazione mondiale.

Lo scopo di questo mio intervento è quello di porre in maggior risalto possibile il necessario sforzo collettivo verso un nuovo Umanesimo Digitale che ponga al centro del dibattito e delle relative scelte risultanti l’uomo inteso come collettività ma, innanzitutto, come singolo individuo.

La scuola, come sempre, può e deve fare da perno e da saldo riferimento per consentire che questi mutamenti epocali indotti nella società siano controllabili e quindi gestibili in maniera tale da consentire ai più di reggere l’urto della già citata rivoluzione. Compito primario della scuola è quello di formare individui, consapevoli delle proprie unicità e possibilità, affinché possano vivere appieno e con soddisfazione nella società. Tale compito, tuttavia, si sta facendo sempre più arduo in un momento in cui le professioni a cui la scuola prepara sono cambiate in maniera radicale e continueranno a cambiare, governate dal ritmo dell’incessante evoluzione tecnologica.

Di sicuro deve esservi l’impegno forte nel ripensare al contesto educativo esaltando una reale didattica basata sulle competenze che riesca a inserire i nostri giovani nel nuovo flusso di richieste di lavoro altamente specializzato. D’altra parte, a monte, si rende assolutamente necessario una rinnovato impegno nella realizzazione di interventi di orientamento sempre più efficienti e motivanti affinché gli studenti possano scegliere percorsi di studio che siano realmente appaganti rispetto alle loro giuste aspettative.

In un contesto già tumultuoso di suo, per l’enormità degli impatti dovuti all’evoluzione digitale, si è innestato negli ultimi mesi, in una sorta di “tempesta perfetta” il cambiamento radicale di vita imposto dal Covid-19. I due elementi si sono in questa situazione, per certi aspetti, fusi l’uno nell’altro: il digitale è stato un baluardo per mantenere una certa normalità di vita, sia all’interno della scuola, ad esempio con la didattica e distanza, sia nella società tutta, attraverso un uso ancora più massiccio dei social e delle videoconferenze.

Le conseguenze di tutto ciò sono state addirittura storiche e probabilmente condizioneranno in maniera sostanziale anche un futuro post Covid. Penso, ad esempio, ai nuovi modelli di lavoro che si sono spostati tra le mura domestiche creando, da una parte, enormi contraccolpi economici nell’ambito di tutto quello che ruotava intorno agli ex luoghi di lavoro fatti di grandi palazzi, quando non grattacieli, e, d’altra parte, nelle enormi possibilità che la libertà di non doversi per forza spostare per lavoro può comportare.

Ma anche in questa ottica dobbiamo mettere al centro questo nuovo Umanesimo Digitale per evitare impatti sociali futuri che potrebbero scuotere nelle fondamenta l’intera nostra società.

Un primo elemento riguarda ovviamente il rischio che solo una nuova élite di lavoratori possa usufruire di tali opportunità, una classe fatta da personale altamente specializzate in quanto i lavori di tipo manuale stanno scomparendo dallo scenario occupazionale.

D’altra parte, penso alla possibilità di riportare vita ai piccoli borghi e paesi, in particolare delle zone più svantaggiate del Sud Italia, facendo sì che si torni ad abitare dai luoghi dai quali si è fuggiti per trovare lavoro nei grandi centri urbani. Ma voglio pensare non più in termini di agglomerati urbani ma piuttosto di agglomerati umani in cui riscoprire il senso del lavoro come strumento per vivere una vita appagante fatta di riscoperti, semplici, autentici, rapporti umani favoriti dal piccolo centro.

Compito nostro, come pensatori e attori della società, è quello di interpretare il tempo corrente e fornire indicazioni utili per tracciare una via che, volendo, sia anche una visione a cui tendere. Il compito, invece, di realizzare in modo concreto queste tensioni e aspettative è affidato, come normale che sia, alla politica che deve guidare attraverso scelte sagge e ponderate al nostro prossimo, immediato, futuro.

In queste scelte vedo, ancora una volta, una rinnovata richiesta di umanesimo digitale che concretizzi l’accesso alla connettività Internet come bene primario e pubblico per tutti i cittadini in tutto il territorio nazionale.

Ancora, missione primaria della politica sarà quella di consentire una efficienza redistribuzione del benessere evitando che il digitale aumenti le differenze già esistenti tra le classi sociali non consentendo che la crescita economica di taluni comporti l’automatico peggioramento delle condizioni di altri.

 

Carlo Mazzone

Carlo A. Mazzone, sviluppatore software e sistemista, è docente di informatica nella Scuola Superiore. Insegnante imprenditivo di Junior Achievement Italia ha ricevuto il JA Italia Top Teacher Lifetime Achievement Award ed è l’unico italiano ad essere arrivato nella Top 10 del Global Teacher Prize, il Nobel dei docenti. È inoltre un digital evangelist e autore di pubblicazioni di divulgazione informatica, best seller in Italia.

Supportaci condividendo sui social il nostro articolo!

Un assaggio di STL con i vettori

“Nell’insegnamento non si può vedere il frutto di una giornata di lavoro. È invisibile e rimane così, forse per venti anni.”
Jacques Barzun

Si dice che i programmatori siano pigri. Di sicuro, se qualcuno ha già lavorato per noi, non si vede il motivo per rifare lo stesso lavoro una seconda volta. Uno di questi “qualcuno”, nel contesto del C++, si chiama Alexander Stepanov, il quale è il principale artefice di una specifica libreria di strumenti realizzata appositamente nella visione della programmazione generica: la Standard Template Library, meglio conosciuta con l’acronimo di STL.

Alexander Stepanov
Alexander Alexandrovich Stepanov, il principale artefice della Libreria STL.

 

Volendo schematizzare al massimo la libreria STL possiamo vederla come composta di tre elementi:

Contenitori
Iteratori
Algoritmi

I contenitori, detti anche in modalità anglosassone containers, sono i veri e propri aggregati di dati. Si tratta quindi di collezioni organizzate di elementi. A scopo di esempio pensate alla struttura dati coda.
Gli iteratori sono degli speciali puntatori che consentono di navigare, cioè di muoversi, all’interno delle strutture dati.
Gli algoritmi sono delle procedure preconfezionate per risolvere i classici problemi che si presentano in relazione alle strutture dati: ordinamento di dati, ricerca di specifici valori, ecc.
Per cercare di essere minimamente concreti di seguito vi propongo un codice minimale relativo all’uso dei vettori. Sebbene il termine vettore venga spesso considerato come sinonimo di array, un vettore (vector) è una struttura dati di tipo parametrico che può essere lavorata in maniera dinamica in quanto, contrariamente ad un array che viene gestito nell’area di memoria stack, esso vive nella sezione di memoria heap.

 

#include <vector>
using namespace std; //oppure devi usare istruzioni del tipo std::vector<int> v;

int main(int argc, char** argv)
{
   printf("I vettori (vectors) non sono array ;)\n\n");
   //Dichiaro un vector (le parentesi angolari sono giustificate dal fatto che si tratta di una classe template)
   //Un vector consente la gestione di memoria contigua permettendo di riferirsi ad essa in un modo del tipo: v[0], v[1]...
   //creiamo un vector per 10 interi (vengono inizializzati a 0)

   int n = 10;

   vector<int> array(n);

   //verifichiamo il suo contenuto
   printf("Vector inizializzato:\n");
   for(int i=0; i<n; i++)
   {
      printf("%d ", array[i]);
   }
   printf("\n\n");

   // valorizziamo il vector con interi da 1 a 10
   int x =1;
   for(int i=0; i<n; i++)
   {
      array[i] = x++;
   }

   //verifichiamo il suo contenuto
   printf("Vector valorizzato con interi da 1 a 10:\n");
   for(int i=0; i<n; i++)
   {
      printf("%d ", array[i]);
   }
   printf("\n\n");

   //inserisco alla fine dell'array un nuovo elemento intero con valore 66
   array.push_back(66);

   //verifichiamo il suo contenuto
   //Usiamo ora la il metodo size() per determinare la nuova dimensione del vector
   printf("Contenuto vector dopo inserimento di un elemento in coda:\n");
   for(int i=0; i<array.size(); i++)
   {
      printf("%d ", array[i]);
   }
   printf("\n\n");

   //ora voglio ridimensionare il vector eliminando così il valore inserito
   //determino quindi l'attuale dimensione del vector
   int temp=array.size();

   //ridimensiono il vector alla dimensione attuale meno 1
   array.resize(temp-1);

   //verifichiamo il contenuto del vettore
   printf("Vector dopo il resize:\n");
   for(int i=0; i<array.size(); i++)
   {
      printf("%d ", array[i]);
   }
   printf("\n\n");

   //Un altro modo per eliminare l'ultimo elemento del vettore
   array.pop_back();
   printf("Vector dopo eliminazione elemento in coda:\n");
   for(int i=0; i<array.size(); i++)
   {
      printf("%d ", array[i]);
   }
   printf("\n");

   return 0;
}


Di seguito l’output del codice in questione:

 

Ovviamente, si tratta di un codice del tutto minimale che ha il solo scopo di far comprendere le potenzialità dei vettori del C++. Nello specifico, dichiariamo innanzitutto il nostro vector affinché contenga 10 elementi interi

int n = 10;
vector<int> array(n);

La prima cosa degna di interesse da notare è poi data dal fatto che inseriamo in coda all’array, in manoera assolutamente dinamica, un nuovo elemento, usando la seguente sintassi.

 

array.push_back(66);

 

Faccio notare, incidentalmente, la classica notazione in stile programmazione orientata agli oggetti dove push_back è il metodo dell’oggetto array.

Ancora di grande interesse è l’effettuazione del ridimensionamento del vettore con l’istruzione resize.

Infine, vi mostro come sia possibile effettuare una sorta di nuovo ridimensionamento dell’array buttando via l’ultimo elemento con l’istruzione:

 

array.pop_back();

 

Ovviamente, come detto, il precedente è un esempio assolutamente minimale che vuole solo sollecitare alla scoperta di modalità differenti per la gestione delle strutture dati.

 

Carlo A. Mazzone

Supportaci condividendo sui social il nostro articolo!