Witam
mam do zrobiernia program działajacy na liczbach zespolonych i nie wiem jak rozwiazac taki problem
uzytkownik ma wpisac wyrazenie w postaci np :
(2+3i)+(4+6i)
a program musi to odpowiednio zinterpretowac i obliczyc, tylko nie wiem jak spowodowac zeby wyluskac z tego tylko potrzebne liczby. Bylbym bardzo wdzieczny za jakakolwiek podpowiedz:)
Ja bym to zrobił w dwóch fazach. Na początku jeżeli między liczbą a i nie ma innego tokenu (może być znak biały) to wstawiłbym " * ". W drugiej fazie po prostu bym to sparsował do ONP zmieniając 'i' na std::complex<double>(0,1).
Polecam Boost.Spirit, który służy właśnie do robienia tego typu prostych parserów w dość prosty sposób. Ponadto opiera się na samych nagłówkach Boosta, więc nie ma potrzeby linkowania do zewnętrznych bibliotek.
Oto jak zaimplementować parser dla typu complex<double>
(kiedyś zrobiłem dla zabawy i nauki). Parsuje liczby zespolone w postaciach (Re, Im i), Re, Im i, Re+Im i na przykład:
(12,45)
1+3i
-i
0.5
-1e4-0.1i
(4-1.2e-3i)
2i
Obsługuje również stałe -- domyślnie tylko Pi i E, ale można łatwo zdefiniować własne w pliku complex_parser_impl.h za pomocą makra REGISTER_CONST
.
complex_parser.h
#pragma once
#include <complex>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
namespace complex
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;
template <typename Iterator>
struct complex_parser : qi::grammar<Iterator, std::complex<double>(), ascii::space_type>
{
complex_parser();
qi::rule<Iterator, std::complex<double>(), ascii::space_type> value;
qi::rule<Iterator, std::complex<double>(), ascii::space_type> imag;
qi::rule<Iterator, double(), ascii::space_type> number;
};
extern complex_parser<std::string::const_iterator> complex_;
}
complex_parser_impl.h
#pragma once
#include "complex_parser.h"
#define REGISTER_CONST(name, value) \
| -lit('+') >> string(#name)[_val = (value)] \
| lit('-') >> string(#name)[_val = -(value)] \
/**/
namespace complex
{
template <typename Iterator>
complex_parser<Iterator>::complex_parser() : complex_parser::base_type(value)
{
using qi::double_;
using qi::string;
using qi::eps;
using qi::_1;
using qi::_val;
using qi::lit;
using phoenix::construct;
number =
double_[_val = _1]
REGISTER_CONST(Pi, 3.1415926535897932384626433832795029)
REGISTER_CONST(E, 2.7182818284590452353602874713526624)
;
imag =
( -lit('+') >> lit('i')[_val = construct<std::complex<double> >(0.0, 1.0)] )
| ( lit('-') >> lit('i')[_val = construct<std::complex<double> >(0.0, -1.0)] )
| ( number[_val = construct<std::complex<double> >(0.0, _1)] >> 'i' )
;
value =
( '(' >> number[_val = construct<std::complex<double> >(_1, 0.0)]
>> ','
>> number[_val += construct<std::complex<double> >(0.0, _1)]
> ')'
)
| ( '(' >> value[_val = _1] > ')' )
| ( imag[_val = _1] )
| ( number[_val = construct<std::complex<double> >(_1, 0.0)]
>> -(
( '+' >> imag[_val += _1] )
| ( '-' >> imag[_val -= _1] )
)
)
;
value.name("value");
number.name("number");
imag.name("imaginary-part");
using phoenix::val;;
qi::on_error<qi::fail>
(
value
, std::cerr
<< val("Error! Expecting ")
<< qi::_4 // what failed?
<< val(" here: \"")
<< construct<std::string>(qi::_3, qi::_2) // iterators to error-pos, end
<< val("\"")
<< std::endl
);
}
}
complex_parser.cpp
#include "complex_parser_def.h"
namespace complex
{
typedef std::string::const_iterator iterator_type;
template complex_parser<iterator_type>::complex_parser();
complex_parser<std::string::const_iterator> complex_;
}
Przykład użycia
#include <iostream>
#include <string>
#include <complex>
#include <vector>
#include "complex_parser.h"
int main(int argc, char* argv[])
{
std::string str;
while (getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::vector<std::complex<double> > numbers;
std::vector<char> operators;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
using complex::complex_;
using complex::qi::char_;
using complex::qi::_1;
using complex::phoenix::push_back;
using complex::phoenix::ref;
bool r = boost::spirit::qi::phrase_parse(
iter,
end,
(
complex_[push_back(ref(numbers), _1)]
% char_("-+*/^")[push_back(ref(operators), _1)]
),
boost::spirit::ascii::space
);
if (r && iter == end)
{
std::cout << "Formula entered:\n";
size_t i = 0;
while (i < operators.size())
{
std::cout << numbers[i] << ' ' << operators[i] << ' ';
++i;
}
std::cout << numbers[i] << '\n';
}
else
std::cerr << "Parsing failed.\n";
}
return 0;
}
Ten kod pozwala na parsowanie liczb zespolonych oddzielonych pojedynczymi znakami +
, -
, *
, /
, ^
. Kolejne liczby są dopisywane do wektora numbers
, a kolejne znaki oddzielające do wektora operators
.