Ci sono già molte buone risposte, quindi il mio affronterà un sottoinsieme della tua domanda; Ovvero, prendo in considerazione la premessa della tua domanda, in quanto OOP e le caratteristiche funzionali non si escludono a vicenda.
Se usi C ++ 11, ci sono molti di questi tipi di funzioni di programmazione funzionale incorporate nella libreria linguaggio / standard che si sincronizzano (abbastanza) bene con OOP. Naturalmente, non sono sicuro di quanto TMP sarà ricevuto dal tuo capo o dai tuoi colleghi, ma il punto è che puoi ottenere molte di queste funzionalità in una forma o in un'altra in linguaggi non funzionali / OOP, come C ++.
L'uso di modelli con ricorsione del tempo di compilazione si basa sui tuoi primi 3 punti,
- Immutabilità
- ricorsione
- Pattern Matching
In questo modello i valori sono immutabili (costanti in tempo di compilazione), ogni iterazione viene eseguita utilizzando la ricorsione e la ramificazione viene eseguita utilizzando (più o meno) la corrispondenza del modello, sotto forma di risoluzione di sovraccarico.
Come per gli altri punti, l'utilizzo di std::bind
e std::function
fornisce un'applicazione di funzione parziale e i puntatori di funzione sono incorporati nella lingua. Gli oggetti chiamabili sono oggetti funzionali (così come l'applicazione di funzioni parziali). Nota che per oggetti chiamabili, intendo quelli che definiscono il loro operator ()
.
La valutazione pigra e le funzioni pure sarebbero un po 'più difficili; per le funzioni pure, puoi usare le funzioni lambda che acquisiscono solo per valore, ma questo non è ideale.
Infine, ecco un esempio di utilizzo della ricorsione in fase di compilazione con l'applicazione di funzioni parziali. È un esempio un po 'forzato, ma dimostra la maggior parte dei punti sopra. Legherà in modo ricorsivo i valori di una determinata tupla a una determinata funzione e genererà un oggetto funzione (chiamabile)
#include <iostream>
#include <functional>
//holds a compile-time index sequence
template<std::size_t ... >
struct index_seq
{};
//builds the index_seq<...> struct with the indices (boils down to compile-time indexing)
template<std::size_t N, std::size_t ... Seq>
struct gen_indices
: gen_indices<N-1, N-1, Seq ... >
{};
template<std::size_t ... Seq>
struct gen_indices<0, Seq ... >
{
typedef index_seq<Seq ... > type;
};
template <typename RType>
struct bind_to_fcn
{
template <class Fcn, class ... Args>
std::function<RType()> fcn_bind(Fcn fcn, std::tuple<Args...> params)
{
return bindFunc(typename gen_indices<sizeof...(Args)>::type(), fcn, params);
}
template<std::size_t ... Seq, class Fcn, class ... Args>
std::function<RType()> bindFunc(index_seq<Seq...>, Fcn fcn, std::tuple<Args...> params)
{
return std::bind(fcn, std::get<Seq>(params) ...);
}
};
//some arbitrary testing function to use
double foo(int x, float y, double z)
{
return x + y + z;
}
int main(void)
{
//some tuple of parameters to use in the function call
std::tuple<int, float, double> t = std::make_tuple(1, 2.04, 0.1);
typedef double(*SumFcn)(int,float,double);
bind_to_fcn<double> binder;
auto other_fcn_obj = binder.fcn_bind<SumFcn>(foo, t);
std::cout << other_fcn_obj() << std::endl;
}