Che cos'è nella programmazione funzionale che fa la differenza?
La programmazione funzionale è in linea di principio dichiarativa . Hai detto quale risultato è invece di come per calcolarlo.
Diamo un'occhiata all'implementazione davvero funzionale del tuo snippet. In Haskell sarebbe:
predsum pred numbers = sum (filter pred numbers)
È chiaro che il risultato è? Abbastanza, è la somma dei numeri che soddisfano il predicato. In che modo viene calcolato? Non mi interessa, chiedi al compilatore.
Potresti dire che usare sum
e filter
è un trucco e non conta. Consentitemi di implementarlo senza questi helper (anche se il modo migliore sarebbe implementarli prima).
La soluzione "Programmazione funzionale 101" che non usa sum
è con 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 che è il risultato in termini di chiamata a funzione singola. È 0
o recursive call + h or 0
, a seconda di pred h
. Ancora piuttosto semplice, anche se il risultato finale non è immediatamente ovvio (anche se con un po 'di pratica questo si legge proprio come un for
loop).
Confronta questo 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, vedo: singola dichiarazione return
, nessuna sorpresa qui: return result
.
Ma cos'è result
? %codice%? Non sembra giusto. Fai qualcosa più tardi con quel int result = 0
. Ok, aggiungi 0
s ad esso. E così via.
Ovviamente, per la maggior parte dei programmatori è abbastanza ovvio che cosa succede in una semplice funzione come questa, ma aggiungi qualche extra% di% di enunciazione e quindi diventa improvvisamente più difficile da tracciare. Tutto il codice riguarda come e cosa è lasciato al lettore per capire - questo è chiaramente uno stile molto imperativo .
Quindi, le variabili e i cicli sono errati?
No.
Ci sono molte cose che sono molto più facili da spiegare e molti algoritmi che richiedono uno stato mutabile sono veloci. Ma le variabili sono intrinsecamente imperative, spiegando come invece di cosa e dando poche previsioni su quale possa essere il loro valore poche righe dopo o dopo alcune iterazioni del ciclo. I loop generalmente richiedono che lo stato abbia senso, e quindi sono intrinsecamente imperativi pure.
Le variabili e i loop non sono semplicemente programmazione funzionale.
Sommario
La programmazione contemporanea funzionale è un po 'più di stile e un modo di pensare utile di un paradigma. La strong preferenza per le funzioni pure è in questa mentalità, ma in realtà è solo una piccola parte.
La maggior parte dei linguaggi diffusi ti consente 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 bene a questo tipo di problemi e rendono semplicemente il codice più breve (e più corto è buono ). Non dovresti sostituire irrispettosamente il codice imperativo con loro, ma quando si adattano, sono quasi sempre una scelta migliore.