Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

8.2. Folding in reverse order

[Note] Note

Note that you can find everything that has been included and defined so far here.

foldl applies a parser repeatedly and iterates over the parsing results from left to right. (This is where the l in the name comes from). Metaparse provides another folding parser combinator, foldr. It applies a parser on the input as well but it iterates from right to left over the results.

Similarly to foldl_start_with_parser, Metaparse provides foldr_start_with_parser as well. A major difference between the two (foldl_start_with_parser and foldr_start_with-parser) solutions is that while foldl_start_with_parser treats the first number as a special one, foldr_start_with_parser treats the last number as a special one. This might sound strange, but think about it: if you want to summarise the elements from right to left, your starting value should be the last element, not the first one, as the first one is the one you visit last.

Due to the above difference foldr_start_with_parser is not a drop-in replacement of foldl_start_with_parser. While the list of values foldl was iterating over is "8", "/ 4", "/ 2", the list of values foldlr has to iterate over is "2", "4 /", "8 /".

This means that the function we use to "add" a new value to the already evaluated part of the expression (this has been binary_op so far) has to be prepared for taking the next operator and operand in a reverse order (eg. by taking "4 /" instead of "/ 4"). We write another metafunction for this purpose:

> template <class S, class Item> \
...> struct reverse_binary_op : \
...>   eval_binary_op< \
...>     typename boost::mpl::at_c<Item, 0>::type, \
...>     boost::mpl::at_c<Item, 1>::type::value, \
...>     S \
...>   > \
...>   {};

copy-paste friendly version

There are multiple differences between binary_op and reverse_binary_op:

We need to include foldr_start_with_parser:

> #include <boost/metaparse/foldr_start_with_parser.hpp>

We can rewrite mult_exp using foldr_start_with_parser:

> using mult_exp3 = \
...> foldr_start_with_parser< \
...>   sequence<int_token, one_of<times_token, divides_token>>, /* The parser applied repeatedly */ \
...>   int_token, /* The parser parsing the last number */ \
...>   boost::mpl::quote2<reverse_binary_op> /* The function called for every result */ \
...>                                         /* of applying the above parser */ \
...> >;

copy-paste friendly version

It is almost the same as mult_exp2, but ...

We can create a new version of exp_parser that uses mult_exp3 instead of mult_exp2:

> using exp_parser17 = \
...> build_parser< \
...>   foldl_start_with_parser< \
...>     sequence<one_of<plus_token, minus_token>, mult_exp3>, \
...>     mult_exp3, \
...>     boost::mpl::quote2<binary_op> \
...>   > \
...> >;

copy-paste friendly version

The only difference between exp_parser17 and the previous version, exp_parser16 is that it uses the updated version of mult_exp. Let's try this parser out:

> exp_parser17::apply<BOOST_METAPARSE_STRING("8 / 4 / 2")>::type
mpl_::integral_c<int, 4>

This version of the parser gives the other possible result. The one you get when division is right associative, which means that the above expression is evaluated as 8 / (4 / 2). Here is a diagram showing how the foldr_start_with_parser-based solution works:

To make it easier to compare the two solutions, here is a diagram showing the two approaches side-by-side:

As we have seen, the associativity of the operators can be controlled by choosing between folding solutions. The folding solutions going from left to right implement left associativity, while the solutions going from right to left implement right associativity.

[Note] Note

Note that folding solutions going from left to right is implemented in a more efficient way than folding from right to left. Therefore when both solutions can be used you should prefer folding from left to right.


PrevUpHomeNext