Cosa c'è nella programmazione funzionale che fa la differenza?
La programmazione funzionale è per principio dichiarativa . Dici qual è il tuo risultato invece di come calcolarlo.
Diamo un'occhiata all'implementazione davvero funzionale del tuo snippet. In Haskell sarebbe:
predsum pred numbers = sum (filter pred numbers)
È chiaro quale sia il risultato? Proprio così, è la somma dei numeri che soddisfano il predicato. Come viene calcolato? Non mi interessa, chiedi al compilatore.
Potresti dire che usare sum
ed filter
è un trucco e non conta. Lascialo implementare senza questi helper (anche se il modo migliore sarebbe implementarli prima).
La soluzione "Programmazione funzionale 101" che non utilizza sum
è con la ricorsione:
sum pred list =
case list of
[] -> 0
h:t -> if pred h then h + sum pred t
else sum pred t
È ancora abbastanza chiaro quale sia il risultato in termini di chiamata a funzione singola. È 0
, o recursive call + h or 0
, a seconda di pred h
. Ancora piuttosto diretto, anche se il risultato finale non è immediatamente ovvio (anche se con un po 'di pratica questo si legge proprio come un for
ciclo).
Confrontalo con la tua versione:
public int Sum(Func<int,bool> predicate, IEnumerable<int> numbers){
int result = 0;
foreach(var item in numbers)
if (predicate(item)) result += item;
return result;
}
Qual'è il risultato? Oh, capisco: singola return
dichiarazione, nessuna sorpresa qui: return result
.
Ma cos'è result
? int result = 0
? Non sembra giusto. Fai qualcosa dopo 0
. Ok, aggiungi item
s. E così via.
Naturalmente, per la maggior parte dei programmatori questo è abbastanza ovvio cosa succede in una semplice funzione come questa, ma aggiungi qualche return
dichiarazione in più e diventa improvvisamente più difficile da tracciare. Tutto il codice riguarda come e cosa deve capire il lettore: questo è chiaramente uno stile molto imperativo .
Quindi, le variabili e i loop sono sbagliati?
No.
Esistono molte cose che sono molto più facili da spiegare e molti algoritmi che richiedono uno stato mutabile per essere veloci. Ma le variabili sono intrinsecamente imperative, spiegano come invece di cosa e dando una piccola previsione di quale possa essere il loro valore poche righe più tardi o dopo alcune ripetizioni cicliche. I circuiti richiedono generalmente che lo stato abbia un senso, e quindi sono anche intrinsecamente imperativi.
Variabili e loop non sono semplicemente una programmazione funzionale.
Sommario
La programmazione contemporanea funzionale è un po 'più di stile e un modo di pensare utile di un paradigma. La forte preferenza per le funzioni pure è in questa mentalità, ma in realtà è solo una piccola parte.
Le lingue più diffuse consentono di utilizzare alcuni costrutti funzionali. Ad esempio in Python puoi scegliere tra:
result = 0
for num in numbers:
if pred(result):
result += num
return result
o
return sum(filter(pred, numbers))
o
return sum(n for n in numbers if pred(n))
Queste espressioni funzionali si adattano perfettamente a quel tipo di problemi e rendono semplicemente il codice più breve (e più breve è buono ). Non dovresti sostituire senza pensarci il codice imperativo con loro, ma quando si adattano, sono quasi sempre una scelta migliore.
item
variabile è mutata nel loop.