2013-02-25 11 views
5

W artykule Boost Phoenix "Transformowanie drzewa wyrażeń", here, zestaw specjalizacji niestandardowej klasy invert_actions, służą do odwracania binarnych wyrażeń arytmetycznych. Na przykład a+b staje się a-b; a*b staje się a/b; i vice versa dla obu.Przekształcanie drzewa stanu C++ Phoenix Expression

Dotyczy to rekursywnego przejścia drzewa wyrażeń - jednak to przejście zatrzymuje się, gdy napotkane zostanie wyrażenie z udziałem operatora, który nie jest jawnie obsługiwany. Na przykład _1+_2-_3 stanie się _1-_2+_3, ale _1+_1&_2 pozostanie bez zmian (nie ma obsługi dla &). let(_a = 1, _b = 2) [ _a+_b ] również pozostanie niezmieniony.

Myślałem, że jest to zgodne z przeznaczeniem artykułu, ale patrząc na testy wymienione na końcu, widzę, że oczekuje się, że zmiana zostanie zmieniona na if_(_1 * _4)[_2 - _3]; z dostarczonym kodem (here), stwierdzam, że tak nie jest.

W jaki sposób można zdefiniować ogólną transformację drzewa ekspresji w funkcji Wzmocnienie Phoenix, która ma zastosowanie do wszystkich zbioru wyraźnie wymienionych (n-ary) operatorów; pozostawiając pozostałe bez zmian?

Niektóre kody mogą być przydatne. Chciałbym następujący kod C++ 11 (auto) do wyjścia 0, a nie 2; bez jawnie obsługujących & lub dowolnego innego operatora/instrukcji.

#include <iostream> 
#include <boost/phoenix.hpp> 
#include <boost/proto/proto.hpp> 

using namespace boost; 
using namespace proto; 
using namespace phoenix; 
using namespace arg_names; 

struct invrt { 
    template <typename Rule> struct when : proto::_ {}; 
}; 

template <> 
struct invrt::when<rule::plus> 
    : proto::call< 
    proto::functional::make_expr<proto::tag::minus>(
     evaluator(_left, _context), evaluator(_right, _context) 
    ) 
    > 
{}; 

int main(int argc, char *argv[]) 
{ 
    auto f = phoenix::eval(_1+_1&_2 , make_context(make_env(), invrt())); 
    std::cout << f(1,2) << std::endl; // Alas 2 instead of 0 
    return 0; 
} 

Odpowiedz

2

W ten sposób można zrobić to z prostej Proto:

#include <iostream> 
#include <boost/phoenix.hpp> 
#include <boost/proto/proto.hpp> 
namespace proto = boost::proto; 
using namespace boost::phoenix; 
using namespace arg_names; 

struct invrt: 
    proto::or_< 
    proto::when< 
     // Turn plus nodes into minus 
     proto::plus<proto::_, proto::_>, 
     proto::functional::make_expr<proto::tag::minus>(
     invrt(proto::_left), invrt(proto::_right) 
    ) 
    >, 
    proto::otherwise< 
     // This recurses on children, transforming them with invrt 
     proto::nary_expr<proto::_, proto::vararg<invrt> > 
    > 
    > 
{}; 

int main(int argc, char *argv[]) 
{ 
    auto f = invrt()(_1+_1&_2); 
    proto::display_expr(f); 
    std::cout << f(1,2) << std::endl; 
    return 0; 
} 

Phoenix warstwowy kilka rzeczy na wierzchu Proto. Nie znam semantyki pheonix::eval lub dlaczego to, co próbowałeś, nie działało. Może ktoś kompetentny Phoenix będzie kurant.

==== EDIT ====

zorientowali się problem na przykładzie Phoenix. To nie jest powtarzanie dla przypadku nie-plus. Kod powinien wyglądać następująco:

#include <iostream> 
#include <boost/phoenix.hpp> 
#include <boost/proto/proto.hpp> 

using namespace boost; 
using namespace proto; 
using namespace phoenix; 
using namespace arg_names; 

struct invrt { 
    template <typename Rule> 
    struct when : 
    // NOTE!!! recursively transform children and reassemble 
    nary_expr<_, vararg<proto::when<_, evaluator(_, _context)> > > 
    {}; 
}; 

template <> 
struct invrt::when<rule::plus> : 
    proto::call< 
    proto::functional::make_expr<proto::tag::minus>(
     evaluator(_left, _context), evaluator(_right, _context) 
    ) 
    > 
{}; 

int main() 
{ 
    auto f = phoenix::eval(_1+_1&_2 , make_context(make_env(), invrt())); 
    display_expr(f); 
    std::cout << f(1,2) << std::endl; // Prints 0. Huzzah! 
} 

czy uważa, że ​​prostsze lub bardziej skomplikowane niż proste rozwiązanie Proto jest, aby zdecydować.

+0

Dzięki @Eric Niebler, to naprawdę fantastyczne - 2 rozwiązania są bardzo hojne. Podoba mi się pierwsze użycie proto, ale drugie użycie szablonowych specjalizacji sprawia, że ​​jest ładnie modułowy; powiedz, jeśli chcę dodać argumenty dotyczące reguły :: dzieli ponownie. – user2023370