Sì, la programmazione funzionale tende a essere difficile da comprendere per molte persone (tendo a dirlo, specialmente quelli che sono già stati prima esposti alla programmazione procedurale).
Direi anche che il tuo esempio di programmazione funzionale non è in realtà un ottimo esempio di programmazione funzionale. Sta usando la ricorsione e sta solo componendo un risultato invece di modificare lo stato, ma non molto di più.
Per ottenere un esempio migliore di programmazione funzionale, considera un problema più generale: piuttosto che "cercare un carattere di sottolineatura e convertire la lettera successiva in maiuscolo", considera questo come solo un caso speciale di ricerca di un modello ed esecuzione di un codice arbitrario quando è stato trovato.
Molte lingue lo supportano, ma per farlo richiedono che specifichiamo il modello come qualcosa di simile a un'espressione regolare. Le espressioni regolari, tuttavia, non sono altro che un linguaggio di programmazione per scopi specifici e un'implementazione di RE è un compilatore e / o interprete per quel linguaggio. Il risultato della compilazione di RE è fondamentalmente una funzione che viene eseguita (in una speciale macchina virtuale RE) per abbinare l'espressione a qualche input.
In qualcosa come Perl, usi un linguaggio speciale per specificare il modello e un compilatore speciale per convertire quella stringa in una sorta di cosa simile a una funzione, e un interprete speciale per prendere quella cosa simile a una funzione per eseguirla. In un linguaggio funzionale, in genere si utilizza il linguaggio stesso per specificare il modello e si utilizza il compilatore del linguaggio stesso per produrre una funzione reale . Siamo in grado di generare quella funzione al volo (circa come se potessimo compilare una RE quando vogliamo), ma quando lo facciamo, il risultato può essere eseguito come qualsiasi altra funzione nella lingua invece di aver bisogno di cose RE speciali per farlo.
Il risultato è che siamo in grado di generalizzare il problema di cui sopra in modo relativamente semplice. Invece di codificare "_" e "maiuscole" direttamente nella trasformazione, tuttavia, possiamo avere qualcosa del tipo:
s&r(pattern, transform, string) {
if (!pattern(string))
return string
else
return transform(matched part of string) + s&r(rest of string);
}
Ma, a differenza di qualcosa in cui specifichiamo il pattern come RE, possiamo specificare il pattern direttamente come una funzione reale, e ancora usarlo, qualcosa come:
my_pattern(string) return beginning(string) == '_';
E poi passiamo quella funzione a s & r. In questo momento, è una funzione piuttosto banale e l'abbiamo codificata interamente staticamente. Un linguaggio funzionale diventa in gran parte interessante quando lo usiamo come possiamo RE, e generiamo una funzione completamente nuova al volo basata su qualcosa come l'input dell'utente, ma a differenza di un RE quella funzione non ha bisogno di un interprete RE speciale per funzionare - è solo una funzione normale come qualsiasi altra.
map
per il passaggio 3 invece di un ciclo mutante. Il secondo approccio è qualcosa che prenderei in considerazione solo se non esiste una funzione split nella libreria standard (nel qual caso dovrebbe essere confrontata con una soluzione imperativa che anche non utilizzasplit
).