Map / fold operators (in C ++)

I am writing a library that can perform mapping / bending operations on ranges. I need to do this with operators. I am not very familiar with functional programming, and I previously selected * for the card and || for fold. So, to find the brute force algorithm, the maximum cos(x) in the interval: 8 < x < 9 :

 double maximum = ro::range(8, 9, 0.01) * std::cos || std::max; 

In the above example, ro::range can be replaced with any STL container.

I do not want to be different if there is any agreement for the map / fold operators. My question is: is there mathematical notation or does any language use the operators for map / fold?

** EDIT **

For those who asked, below is a small demonstration of what RO can do now. scc is a small utility that can evaluate C ++ fragments.

 // Can print ranges, container, tuples, etc directly (vint is vector<int>) : scc 'vint V{1,2,3}; V' {1,2,3} // Classic pipe. Alogorithms are from std:: scc 'vint{3,1,2,3} | sort | unique | reverse' {3, 2, 1} // Assign 42 to [2..5) scc 'vint V=range(0,9); range(V/2, V/5) = 42; V' {0, 1, 42, 42, 42, 5, 6, 7, 8, 9} // concatenate vector of strings ('add' is shotcut for std::plus<T>()): scc 'vstr V{"aaa", "bb", "cccc"}; V || add' aaabbcccc // Total length of strings in vector of strings scc 'vstr V{"aaa", "bb", "cccc"}; V * size || (_1+_2)' 9 // Assign to c-string, then append `"XYZ"` and then remove `"bc"` substring : scc 'char s[99]; range(s) = "abc"; (range(s) << "XYZ") - "bc"' aXYZ // Remove non alpha-num characters and convert to upper case scc '(range("abc-123, xyz/") | isalnum) * toupper' ABC123XYZ // Hide phone number: scc "str S=\"John Q Public (650)1234567\"; S|isdigit='X'; S" John Q Public (XXX)XXXXXXX 
+6
source share
3 answers

Of the languages ​​that I know, there is no standard way of folding. Scala uses the /: and :\ operators, as well as the method names, Lisp has reduce , Haskell has foldl .

map , on the other hand, is more often found simply as map in all the languages ​​that I know.

+5
source

This is more of a comment than a true answer, but it's too long to fit into a comment.

At least if my memory for terminology is working correctly, the map is essentially std::transform , and fold is std::accumulate . Assuming that is correct, I think that trying to write your own will be bad at best.

If you want to use map / fold style semantics, you can do something like this:

 std::transform(std::begin(sto), std::end(sto), ::cos); double maximum = *std::max_element(std::begin(sto), std::end(sto)); 

Although std::accumulate more like a universal fold, std::max_element is basically equal to fold(..., max); If you prefer a single operation, you can do something like:

 double maximum = *(std::max_element(std::begin(sto), std::end(sto), [](double a, double b) { return cos(a) < cos(b); }); 

I urge you to review overload operators for this purpose. Any of the above examples should be understood by almost any reasonable C ++ programmer. The example you specified will be completely opaque to most.

On a more general level, I urge excessive caution when overloading statements. Operator overloading is excellent when used correctly - the ability to overload operators for things like precision integers, matrices, complex numbers, etc., makes code using these types more understandable and understandable than code without overloaded operators.

Unfortunately, when you use operators in unexpected ways, the opposite is true, and this use is, of course, extremely unexpected - in fact, in the "pretty amazing" range. A question may arise (but at least a little justification) if these operators were well understood in certain areas, but unlike other C ++ applications. In this case, however, you seem to be inventing the “whole fabric” notation — I don't know anyone who uses any kind of operator. C ++ supports overloading to mean either a crease or a map (or nothing visually similar or similar in any other way). In short, using overload in this way is a bad and unreasonable idea.

+9
source

The following is the implementation of fold in C ++ syntax. Please note that the code is not very reliable and serves only to demonstrate the point. It is designed to support the more common 3-argument fold operators (range, binary operation, and neutral).

It's easy to laugh at how to abuse (did you just say "rape"?) Operator overload and one of the best ways to shoot with a 900 pound artesian projectile.

  enum { fold } fold_t; template <typename Op> struct fold_intermediate_1 { Op op; fold_intermediate_1 (Op op) : op(op) {} }; template <typename Cont, typename Op, bool> struct fold_intermediate_2 { const Cont& cont; Op op; fold_intermediate_2 (const Cont& cont, Op op) : cont(cont), op(op) {} }; template <typename Op> fold_intermediate_1<Op> operator/(fold_t, Op op) { return fold_intermediate_1<Op>(op); } template <typename Cont, typename Op> fold_intermediate_2<Cont, Op, true> operator<(const Cont& cont, fold_intermediate_1<Op> f) { return fold_intermediate_2<Cont, Op, true>(cont, f.op); } template <typename Cont, typename Op, typename Init> Init operator< (fold_intermediate_2<Cont, Op, true> f, Init init) { return foldl_func(f.op, init, std::begin(f.cont), std::end(f.cont)); } template <typename Cont, typename Op> fold_intermediate_2<Cont, Op, false> operator>(const Cont& cont, fold_intermediate_1<Op> f) { return fold_intermediate_2<Cont, Op, false>(cont, f.op); } template <typename Cont, typename Op, typename Init> Init operator> (fold_intermediate_2<Cont, Op, false> f, Init init) { return foldr_func(f.op, init, std::begin(f.cont), std::end(f.cont)); } 

foldr_func and foldl_func (actual left and right fold algorithms) are defined elsewhere.

Use it as follows:

 foo myfunc(foo, foo); container<foo> cont; foo zero, acc; acc = cont >fold/myfunc> zero; // right fold acc = cont <fold/myfunc< zero; // left fold 

The word fold used as some poor human new reserved word here. You can define several variations of this syntax, including

 <<fold/myfunc<< >>fold/myfunc>> <foldl/myfunc> <foldr/myfunc> |fold<myfunc| |fold>myfunc| 

The internal statement must have the same or higher priority as the external (s). This is a limitation of C ++ grammar.

map requires only one intermediate link, and the syntax can be, for example,

 mapped = cont |map| myfunc; 

Implementation is a simple exercise.

Oh, and please do not use this syntax in production if you do not know very well what you are doing, and perhaps even if you do it;)

+2
source

All Articles