Il libro "The Pragmatic Programmer" (se non l'avete letto, smettete di leggere questo articolo e fatelo subito!) dice che ogni anno dovremmo imparare un nuovo linguaggio di programmazione.
Anche se alcuni potrebbero obiettare che si tratta di uno sforzo eccessivo, siamo tutti d'accordo che potrebbe essere una buona idea. Scegliere una nuova lingua da imparare non è così facile. Non vogliamo dedicare il nostro tempo a qualcosa che forse non useremo mai nella pratica, vero? Ma forse a volte dovremmo fare un'eccezione e imparare qualcosa solo per il gusto di farlo? Vorrei presentarvi il linguaggio Brainfuck. È un linguaggio che si impara in un paio di minuti, quindi non c'è il problema di investire troppo tempo invano. Inoltre, posso promettere che la soluzione di qualsiasi problema con Brainfuck stimolerà il vostro cervello (tutte le fregature sono solo un bonus ;)). Iniziamo! Secondo Wikipedia:
Brainfuck è un linguaggio di programmazione esoterico creato nel 1993 da Urban Müller. Il linguaggio è composto solo da otto semplici comandi e da un puntatore di istruzioni. Pur essendo completamente Turing-completo, non è destinato a un uso pratico, ma a sfidare e divertire i programmatori.
Panoramica della lingua
Immaginate un nastro infinitamente lungo composto da celle, ognuna inizializzata a 0. C'è anche un puntatore mobile ai dati che inizialmente punta alla prima cella. Ci sono anche due flussi di byte per l'input e l'output. Le istruzioni vengono eseguite in sequenza, una alla volta. La macchina si ferma dopo aver eseguito l'ultima.
Comando
Cosa fa?
>
sposta il puntatore dei dati alla cella successiva a destra
<
sposta il puntatore dei dati alla cella successiva a sinistra
+
incrementare il valore della cella corrente
–
decrementa il valore della cella corrente
.
emette il byte di una cella attualmente puntata in ASCII codice
,
legge un byte da stdin e memorizza il suo valore nella cella corrente
[
se la cella corrente è 0, salta alla cella corrispondente ]
]
salta alla corrispondenza [
Tutti i caratteri diversi da ><+-.,[] vengono ignorati.
Vediamo un semplice esempio:
,+.
Sarà interpretato come segue:
leggere un byte e memorizzarlo nella cella corrente (cella0)
incrementare il valore della cella corrente (cell0 = cell0 + 1)
scrivere il contenuto della cella corrente in uscita
Di conseguenza, viene letto un carattere dall'ingresso e viene stampato il carattere successivo dalla tabella ASCII.
Interprete / Compilatore
Prima di scrivere programmi utili (?) in Brainfuck, abbiamo bisogno di un interprete o di un compilatore. AFAIK, non ne esiste uno ufficiale, ma non è un problema. Ce ne sono decine non ufficiali in Internet. Posso raccomandare questi due:
"Hello World!" dovrebbe essere il primo programma che scriviamo quando impariamo un nuovo linguaggio. Tuttavia, scriverlo in Brainfuck è un po' più difficile che in altri linguaggi. Dobbiamo iniziare con qualcosa di più semplice... Scriviamo un programma che stampi sullo schermo una singola lettera "H" (così eccitante :D):
Come funziona? Imposta il valore della cella corrente a 72 (eseguendo 72 incrementi) e lo stampa sullo schermo utilizzando "." (H ha codice 72 in ASCII). Ora sapete che cosa dovremmo fare per stampare "Hello World!" sullo schermo, ma prima di questo faremo un piccolo refactoring. Scrivere tutti quei '+' richiede troppe operazioni di digitazione e conteggio. Possiamo accorciare i tempi utilizzando [ e ] per l'esecuzione di cicli. Per impostare il valore a 72 possiamo, ad esempio, eseguire un ciclo che aumenta il valore 7 volte di 10. In questo modo otteniamo 70. In questo modo otteniamo 70. Aggiungendo 2 si ottiene 72. L'aspetto è il seguente:
++++++++++ # imposta la cella0 su 10
[# ciclo fino a quando la cella0 è 0
- # diminuisce la cella0
> # sposta il puntatore dati a destra (cella1)
+++++++ # aumenta la cella1 di 7
# sposta il puntatore dati a destra (cella1)
++ # aumenta di 2
. # stampa il risultato
Ho inserito dei commenti per chiarire il funzionamento del programma. Lo stesso programma senza commenti:
++++++++++[->+++++++++.
Non è bellissimo? 🙂
Ciao mondo!
Torniamo al nostro programma "Hello World!". Potremmo impostare il valore della prima cella a 72 (H) e stamparlo, impostare il valore della seconda cella a 101 (e) e stamparlo, impostare il valore della terza cella a 108 e stamparlo e così via. Ecco l'implementazione di questo algoritmo:
Già, solo 1120 byte per stampare "Hello World!"... Ma possiamo fare di meglio! Invece di usare una nuova cella per ogni carattere, usiamone una sola. Per stampare la lettera "e" (101) possiamo riutilizzare il valore della cella0 (72). Possiamo aumentarlo di uno per 29 volte (101 - 72). Il risultato è il seguente:
È solo 106 byte e stampa una nuova riga alla fine! Incredibile.
Invertire una stringa
Ora siamo pronti a scrivere qualcosa di più impegnativo. Scriviamo un programma che legga una riga dall'input e la stampi in ordine inverso. Il primo problema è leggere i caratteri e fermarsi sul carattere della nuova riga. Ricordate che non c'è pausa, se o altre dichiarazioni simili. Dobbiamo utilizzare [ e ]. Cominciamo con un programma che legge tutti i caratteri dall'input e li inserisce in celle successive:
,[>,]
Inizia con la lettura del primo carattere e continua fino all'ultimo. , ritorno dell'operazione 0. Tuttavia, andrà in loop per sempre nell'implementazione che restituisce qualcosa di diverso da O per EOF (il linguaggio non specifica questo comportamento). Come possiamo quindi fermarci sul carattere di nuova riga? Ecco il trucco:
+[++++++++++>,----------]
Iniziamo con la cella0 impostata a 1 per assicurarci che il nostro ciclo venga eseguito almeno una volta. In un ciclo aumentiamo il valore della cella corrente di 10, spostiamo il puntatore dati alla cella successiva, leggiamo un carattere e diminuiamo il suo valore di 10. In questo modo, se viene letto un carattere di nuova riga (10 in ASCII), il programma si fermerà in un'iterazione successiva. In questo modo, se viene letto un nuovo carattere di riga (10 in ASCII), il programma si fermerà alla prossima iterazione, altrimenti il suo valore sarà ripristinato aggiungendo 10.
Dopo questo passaggio, le nostre celle avranno il seguente aspetto:
11 C1 C2 C3 0* 0 0
Cn è l'ennesimo carattere dell'input e * è la posizione attuale del puntatore ai dati. Ora dobbiamo iniziare a spostare il puntatore dei dati a sinistra e stampare tutte le celle fino a raggiungere il valore 11. Ecco la mia interpretazione del compito:
Quando mi sono imbattuto in Brainfuck, un linguaggio di programmazione esoterico, inizialmente l'ho liquidato come un semplice espediente o uno scherzo. Questo linguaggio così particolare e, come molti potrebbero sostenere, così difficile da capire, mi è apparso come qualcosa destinato solo al divertimento. Col tempo, però, la mia percezione di Brainfuck è cambiata radicalmente.
La natura enigmatica di Brainfuck vi mette alla prova, spingendovi ad ampliare la vostra prospettiva su linguaggi di programmazione. Questo linguaggio esoterico permette di apprezzare la bellezza e la necessità dei linguaggi di alto livello a cui siamo abituati. Mette in evidenza l'importanza delle astrazioni, delle convenzioni di denominazione e di una disposizione organizzata della memoria nel regno dei linguaggi di programmazione. Questo è qualcosa che Brainfuck, con il suo design minimalista composto da soli otto semplici comandi, non fornisce.
Brainfuck è un linguaggio completo di Turing che sottolinea ulteriormente l'importanza di avere un codice sorgente chiaro e coerente. Nonostante sia riconosciuto come uno dei linguaggi esoterici più impegnativi in cui scrivere programmi, è ironicamente il preferito dai principianti per chiunque voglia creare un compilatore o un interprete Brainfuck. Il motivo è la semplicità del suo set di comandi e il fatto che non richiede un parsing complesso.
La creazione di un programma Brainfuck è unica per due aspetti. In primo luogo, è necessario adattarsi all'uso di un singolo puntatore di memoria, che costringe a pensare in modo diverso al codice sorgente. In secondo luogo, si dispone dell'"opzione zero", ovvero la possibilità di azzerare la cella di memoria, una caratteristica non comune ad altri linguaggi di programmazione formale.
In termini di apprendimento, con Brainfuck c'è molto di più di quanto si possa immaginare. Se si ha tempo a sufficienza e la giusta mentalità, è possibile scrivere lo stesso programma in una moltitudine di modi utilizzando codici Brainfuck diversi. L'ultima metà di questo viaggio consiste nell'essere inventivi e nel trovare nuovi modi creativi per utilizzare i sei simboli.
Gli interpreti di Brainfuck, pur essendo minimalisti, consentono di comprendere a fondo come viene eseguito il codice, cosa stampa il programma e la meccanica sottostante di un linguaggio completo di Turing. Alla fine, Brainfuck non è solo un altro linguaggio di programmazione esoterico. È una dimensione completamente nuova, un modo diverso di vedere, capire e scrivere i programmi.