This semester learned a functional language Coq, invented by the French, it is similar to ML, in fact, Coq's invention is mainly affected by the ML. Coq Grammar I will not be here to describe, interested friends can read this book software Foundations, which is the teaching materials we use. Let's take a look at some examples below.
1. Map
Its function is similar to the algorithm in C + + std::transform , is to f function in list each element (element type is X ), return one list (element type is Y ), which f is a unary function, the parameter type is, the X return value type is Y. One thing to note is that this is not related map list to the container in the STL, because the COQ is so named in the source program, so I used it.
Fixpoint map {X Y:Type} (f:X->Y) (l:list X) : (list Y) := matchwith | [] => [] | h :: t => (f h) :: (map f t) end.
I had a whim to implement the above features in C + +. The procedure is as follows.
template <classclasstemplate <classclassclass List>automap(UnaryOperation f, List<X> l){ if (l.empty()) { return List<decltype(f(X()))>(); } X h = l.front(); l.pop_front(); automap(f, l); ret.push_front(f(h)); return ret;}
In writing this function, my greatest feeling is that the automatic type derivation of the functional language is particularly powerful, and when rewritten in C + +, it has to be implemented using generic technology, the above code with the C++14 standard.
2. Fold
The following is a function map opposite to the function fold , it is the function of the list elements in the approximate, similar to the STL std::accumulate . The code for COQ is as follows.
Fixpoint fold {X Y:Type} (f: X->Y->Y) (l:list X) (b:Y) : Y := matchwith | nil => b | h :: t => f h (fold f t b) end.
Accordingly, the following program is rewritten with C + +.
template <classclassclass List>Y fold(BinaryOperation f, List l, Y b){ if (l.empty()) { return b; } auto h = l.front(); l.pop_front(); return f(h, fold(f, l, b));}
3. Fold_map
When learning Coq, this function is an exercise and requires fold implementation map . I just started to see this topic when it is very magical, fold is "getting smaller", map is "getting bigger", how fold can map it be achieved? In fact, the key lies fold in the one f , its type can be seen as X->Y->Y , here X and Y can be different. For example, X it can be a natural number, Y it can be list , this is "getting bigger". Implemented as follows.
listlist Y := fold (fun x l => (f x) :: l) l [].
The corresponding C + + code is as follows.
/* * use fold to implement map */template <classclasstemplate <classclassclass List>auto fold_map(UnaryOperation f, List<X> l){ using L = List<decltype(f(X()))>; auto bin_fun = [&f] (X x, L lst) { lst.push_front(f(x)); return lst; }; return fold(bin_fun, l, L());}
It is common to use LAMBDA expressions in functional languages, and the catch list does not have to be specified, the syntax is simple, but its internal implementation is very heavy, so the efficiency is not high C + +.
4. Using STL to implement fold and map
It says that the map fold function is similar to that std::transform of std::accumulate function respectively. So you can also use them to achieve, the program is as follows.
Template<classYclassBinaryoperation,classList>y fold (binaryoperation F, List L, Y b) {//Reverse parameter list in F AutoBinary_op = [&f] (AutoAAutob) {returnF (b, a); };returnAccumulate (Crbegin (L), Crend (L), B, binary_op);Template<classXclassUnaryoperation,Template<class,class...>classList>Auto Map(Unaryoperation F, list<x> L) {list<Decltype(f (X ())) > Dst_list (L.size ()); Transform (Cbegin (L), Cend (L), Begin (Dst_list), f);returnDst_list;}
foldIn fact std::accumulate , there are two subtle differences between the two. The first is fold the type of formal parameter f X->Y->Y , and the std::accumulate type in is f defined as Y->X->Y , if you do not understand the words directly see std::accumulate the implementation of it.
template <classclassclass BinaryOperation>Tp accumulate(InputIterator first, InputIterator last, Tp init, BinaryOperation binary_op){ for ( ; first != last; ++first) { init = binary_op(init, *first); } return init;}
So you have to transform the order of the parameters when you implement it. The second difference is that it is executed from the back std::accumulate , and then from the back fold , why fold is it from the back forward? This list is related to the definition:
list (X:Type) : Type := list X listlist X.Arguments cons {X} _ _. (* use underscore for argument position that has no name *)"x :: y" := (cons x y) 60, right associativity).
The cons type above is forall X : Type, X -> list X -> list X , meaning that the first parameter is an element, the second argument is list , then the result is that list this is the root cause, leading to the fold execution from the back forward. Therefore std::accumulate , the implementation can be implemented with a reverse iterator from the back forward.
5. References
[1] software foundations
[2] C + + Primer
[3] STL Source code Analysis
6. Appendix (source program)
#include <iostream>#include <list>#include <iterator>#include <algorithm>#include <string>#include <cassert>using namespace STD;Template<classYclassBinaryoperation,classList>y fold (binaryoperation F, List L, Y b) {if(L.empty ()) {returnb }Autoh = L.front (); L.pop_front ();returnF (H, fold (F, L, b));}Template<classXclassUnaryoperation,Template<class,class...>classList>Auto Map(Unaryoperation F, list<x> L) {if(L.empty ()) {returnlist<Decltype(f (X ())) > (); } X H = L.front (); L.pop_front ();AutoRET =Map(f, L); Ret.push_front (f (h));returnRET;}/ * * Use fold to implement map * /Template<classXclassUnaryoperation,Template<class,class...>classList>AutoFold_map (unaryoperation F, list<x> l) {usingL = list<Decltype(f (X ())) >;AutoBin_fun = [&f] (x x, L lst) {Lst.push_front (f (x));returnLst };returnFold (Bin_fun, L, L ());}intMainintargcChar**ARGV) {Autof = [] (Autox) {return "element is"+ to_string (x); }; list<short >L = {1,2};AutoLST = Fold_map (f, L); Copy (Lst.begin (), Lst.end (), ostream_iterator<Decltype(LST)::value_type> (cout,"\ n")); ASSERT (Fold_map (f, l) = =Map(f, L));return 0;}
#include <iostream>#include <numeric>#include <deque>#include <iterator>#include <algorithm>#include <string>#include <cassert>using namespace STD;Template<classYclassBinaryoperation,classList>y fold (binaryoperation F, List L, Y b) {//Reverse parameter list in F AutoBinary_op = [&f] (AutoAAutob) {returnF (b, a); };returnAccumulate (Crbegin (L), Crend (L), B, binary_op);Template<classXclassUnaryoperation,Template<class,class...>classList>Auto Map(Unaryoperation F, list<x> L) {list<Decltype(f (X ())) > Dst_list (L.size ()); Transform (Cbegin (L), Cend (L), Begin (Dst_list), f);returnDst_list;}/ * * Use fold to implement map * /Template<classXclassUnaryoperation,Template<class,class...>classList>AutoFold_map (unaryoperation F, list<x> l) {usingL = list<Decltype(f (X ())) >;AutoBin_fun = [&f] (x x, L lst) {Lst.push_front (f (x));returnLst };returnFold (Bin_fun, L, L ());}intMainintargcChar**ARGV) {Autof = [] (Autox) {return "element is"+ to_string (x); }; deque<short >L = {1,2};AutoLST = Fold_map (f, L); Copy (Cbegin (LST), Cend (LST), ostream_iterator<Decltype(LST)::value_type> (cout,"\ n")); ASSERT (Fold_map (f, l) = =Map(f, L));return 0;}
Functional programming and C + +