Articoli di programmazione

Programmazione funzionale in C# - prima parte

C# è un linguaggio multi-paradigma, vediamo come si adatta a quello funzionale

circuiti.jpg
Programmazione imperativa e funzionale sono due paradigmi che possono essere utilizzati per risolvere problemi in maniera differente.

La #programmazione imperativa è basata sull'utilizzo di istruzioni che specificano COME deve essere eseguito il programma. In questa modalità di programmazione, lo sviluppatore definisce in modo dettagliato le operazioni che il programma deve eseguire per risolvere un problema. In genere, questo viene fatto tramite l'utilizzo di cicli, condizioni, assegnazioni di variabili e altre istruzioni. L'obiettivo della programmazione imperativa è di creare un elenco di istruzioni che il computer deve eseguire in modo sequenziale per ottenere il risultato desiderato.

La programmazione funzionale è basata sull'utilizzo di funzioni per definire le operazioni che il programma deve eseguire. In questo #paradigma, il programma è diviso in una serie di funzioni che elaborano i dati in input e producono dei risultati. Queste funzioni possono essere composte tra loro per risolvere problemi più complessi. La programmazione funzionale idealmente si concentra sull'uso di funzioni pure, ovvero funzioni che non hanno effetti collaterali e producono sempre lo stesso risultato per gli stessi input.

Le differenze principali tra la programmazione imperativa e quella funzionale quindi a grandi linee sono le seguenti:

- In programmazione imperativa, lo sviluppatore definisce in modo dettagliato le operazioni che il programma deve eseguire, mentre in programmazione funzionale, lo sviluppatore definisce ed orchestra le funzioni che elaborano i dati in input e producono dei risultati.

- In programmazione imperativa, lo stato viene gestito tramite variabili, mentre in programmazione funzionale, le funzioni non hanno stati interni e non modificano i dati in input.

- In programmazione imperativa, le operazioni sono eseguite in modo sequenziale, mentre in programmazione funzionale, le operazioni possono essere eseguite in modo parallelo e non sequenziale.

- In programmazione imperativa, gli effetti collaterali sono comuni, mentre in programmazione funzionale, si cerca di evitare gli effetti collaterali.

In informatica si dice che una funzione produce un effetto collaterale quando modifica un valore o uno stato al di fuori del proprio scoping locale. Per esempio, una funzione ha un effetto collaterale quando modifica una variabile globale o statica, quando modifica uno dei suoi argomenti, quando scrive dati su di un display, su di un file, un database, fa una chiamata ad una API o quando invoca altre funzioni con a loro volta hanno effetti collaterali.
Ovviamente un programma normalmente esegue molti effetti collaterali ma nel paradigma funzionale questi sono ridotti al minimo, quando non eliminati del tutto; sono ben delimitati in apposite funzioni, chiamate in punti e momenti ben precisi.

C# è un #linguaggio di programmazione principalmente imperativo, ma ha anche alcune funzionalità che consentono di scrivere codice in modo #funzionale. Ecco un esempio di codice che utilizza alcune funzionalità di C# per scrivere in modo funzionale con l'apporto essenziale di #LINQ:


using System;
using System.Linq;

class Program
{
static void Main()
{
int[] numbers = { 1, 2, 3, 4, 5 };

// Esempio di utilizzo di una funzione lambda per filtrare i numeri pari
var evenNumbers = numbers.Where(n => n % 2 == 0);

// Esempio di utilizzo di una funzione lambda per sommare i numeri
var sum = numbers.Aggregate((acc, n) => acc + n);

Console.WriteLine($"Numeri pari: {string.Join(", ", evenNumbers)}");
Console.WriteLine($"Somma dei numeri: {sum}");
}
}


In questo esempio, viene utilizzata la funzione `Where` per filtrare solo i numeri pari dall'array `numbers`, usando una funzione lambda per definire il criterio di filtraggio. Inoltre, viene utilizzata la funzione `Aggregate` per sommare tutti i numeri dell'array, usando una funzione lambda per definire l'operazione di somma.

Questo è solo un semplice esempio di come C# può essere utilizzato per scrivere codice in modo funzionale. La programmazione funzionale richiede solitamente una diversa mentalità di programmazione rispetto alla #programmazione #imperativa, ma può portare a codice più elegante, modulare e facile da testare.

Vediamo ora la differenza tra una semplice algoritmo funzionale ed imperativo.
Ecco la versione funzionale, leggere bene i commenti:

using System;
using System.Linq;

class Program
{
static void Main()
{
string[] names = { "Alice", "Bob", "Charlie", "David", "Eve" };

// Esempio di utilizzo di una catena di funzioni per manipolare la lista di nomi
var result = names
.Where(name => name.Length <= 4) // Filtra solo i nomi con lunghezza <= 4
.Select(name => name.ToUpper()) // Trasforma i nomi in maiuscolo
.Aggregate((acc, name) => acc + ", " + name); // Concatena i nomi separandoli con una virgola

Console.WriteLine($"Risultato: {result}");
}
}

In questo esempio, viene definita una lista di stringhe names. Viene poi utilizzata una catena di funzioni per manipolare la lista in vari modi:

- La funzione Where viene utilizzata per filtrare solo i nomi con lunghezza <= 4.
- La funzione Select viene utilizzata per trasformare i nomi in maiuscolo.
- La funzione Aggregate viene utilizzata per concatenare i nomi in una singola stringa, separandoli con una virgola.

Ed ora quella imperativa:

using System;

class Program
{
static void Main()
{
string[] names = { "Alice", "Bob", "Charlie", "David", "Eve" };

string result = "";

foreach (string name in names)
{
if (name.Length <= 4)
{
result += name.ToUpper() + ", ";
}
}

if (result.Length > 2)
{
result = result.Substring(0, result.Length - 2);
}

Console.WriteLine($"Risultato: {result}");
}
}

In questo esempio, viene definita una lista di stringhe names. Viene poi utilizzato un ciclo foreach per iterare attraverso ogni nome nella lista. Per ogni nome, viene verificato se la sua lunghezza è minore o uguale a 4. Se il nome rispetta questa condizione, viene trasformato in maiuscolo e concatenato alla stringa result.

Una volta completata l'iterazione, viene verificato se la stringa Result contiene almeno un nome concatenato. Se sì, l'ultima virgola e lo spazio vengono rimossi dalla stringa.

Chiare le differenze di approccio?

#csharp #programmazione #development #paradigma #funzionale #coding #architettura #codice