I'm going to parse a string of mathematical functions in Qt 5.12, for example
"fabs( sin( pow( cos(30) , pow(1,2) ) ) )"
How do I parse out each function name and its arguments correctly, continue parsing when the function argument is another function, and call a library function with the same name for a numeric value? Finally, the result is returned to the function above.
I tried to write code like this, with ", "as the separator to get each parameter. But when the argument is a function that requires more than one argument, a BUG occurs. But I can't think of a solution
double Cal::calculate(const QString & funcstr)
{
int lb = funcstr.indexOf("(");
int rb = funcstr.lastIndexOf(")");
double result = 0;
if(funcstr.startsWith("pow", Qt::CaseInsensitive))
{
QStringList paramList_str = funcstr.mid(lb + 1, rb - lb - 1).split(",");
QList<double> paramList;
for(int i = 0; i < paramList_str.size(); ++i)
{
if(parse(paramList_str[i]))
{
paramList.push_back(paramList_str[i].toDouble());
}else{
paramList.push_back(calculate(paramList_str[i]));
}
}
result = pow(paramList[0], paramList[1]);
}
else if(funcstr.startsWith("sin", Qt::CaseInsensitive))
{
QString param = funcstr.mid(lb + 1, rb - lb - 1);
if(parse(param))
result = sin(param.toDouble());
else
result = sin(calculate(param));
}else if(funcstr.startsWith("cos", Qt::CaseInsensitive))
{
QString param = funcstr.mid(lb + 1, rb - lb - 1);
if(parse(param))
result = cos(param.toDouble());
else
result = cos(calculate(param));
}else if(funcstr.startsWith("tan", Qt::CaseInsensitive))
{
QString param = funcstr.mid(lb + 1, rb - lb - 1);
if(parse(param))
result = tan(param.toDouble());
else
result = tan(calculate(param));
}
retur result;
}
bool parse(const QString &funcName)
{
QChar c = funcName[0];
bool res = true;
if(c.isLetter())
res = false;
return res;
}
Related
question:
Write a recursive method flgIsSorted to check if a given array (provided as a parameter) is sorted in increasing order. The method returns true if and only if the array is sorted in increasing order. Hint, when the array has only one element, it is sorted. If the first half is sorted, the second half is sorted, and the first element of the second half is not smaller than the last element in the first half, the array is sorted. Your initial method can only take one parameter – the array. That method can call another auxiliary method that takes other parameters.
public boolean flgIsSorted(int a[], int startIndex, int endIndex ){
boolean result = false;
if(startIndex < endIndex){
int mid = (startIndex + endIndex)/2;
flgIsSorted(a, startIndex, mid);
flgIsSorted(a, mid+1, endIndex);
result = check(a, startIndex, mid, endIndex);
}
return result;
}
public boolean check(int a[], int startIndex, int mid, int endIndex){
//deal with left array
//If array has odd number of elements,
//left array will be even number
//and right array will be odd number
int n1 = mid - startIndex + 1;
// n1 is index, and we need n1 + 1 spots for copy array
int L[] = new int[n1 + 1];
//copy subarray A[p..q] into L[0..n1],
//i starts from the beginning of unsorted array
for(int i = startIndex; i <= mid + 1; i++){
//make sure copy to the index 0 of left array
L[i - startIndex] = a[i];
}
L[n1] = Integer.MAX_VALUE;
//deal with right array
int n2 = endIndex - mid;
int R[] = new int[n2 + 1];
//copy subarray A[q+1..r] into R[0..n2]
for(int j = mid + 1; j <= endIndex; j++){
//make sure start from the index 0 of right array
R[j - (mid + 1)] = a[j];
}
R[n2] = Integer.MAX_VALUE;
int i = 0;
int j = 0;
boolean result = false;
for(int k = startIndex; k <= endIndex; k++){
if(L[i] < R[j]){
//a[k] = L[i];
i++;
result = true;
System.out.println("true in check");
}else{
//a[k] = R[j];
j++;
System.out.println("false in check");
result = false;
}
}
System.out.println("return in check final");
return result;
}
Problem:
It always returns true.
I think I just figured it out this morning. At least the output is what I want now.
Code:
public boolean flgIsSorted(int a[], int startIndex, int endIndex){
boolean result = false;
if(a.length == 1){
result = true;
}else{
if(startIndex < endIndex){
int mid = (startIndex + endIndex)/2;
if(a[startIndex] <= a[mid + 1]){
result = true;
}else{
result = false;
}
flgIsSorted(a, startIndex, mid);
flgIsSorted(a, mid + 1, endIndex);
}
}
return result;
}
i am using arduino due. what i am trying to do is to receive a string at serial. like this one:
COMSTEP 789 665 432 END
if the string starts with comstep, then to tokenize the string and get an integer array {789, 665, 432}.
is there anyway to do that?
P.S: im a noob at programming, so any help is appreciated.
I have a function that I wrote long ago to parse strings up in an easy manner. It is in use on several of my Arduino projects.
Sample usage:
char pinStr[3];
char valueStr[7];
int pinNumber, value;
getstrfld (parms_in, 0, 0, (char *)",", pinStr);
getstrfld (parms_in, 1, 0, (char *)",", valueStr);
pinNumber = atoi (pinStr);
value = atoi (valueStr);
The functions:
// My old stand-by to break delimited strings up.
char * getstrfld (char *strbuf, int fldno, int ofset, char *sep, char *retstr)
{
char *offset, *strptr;
int curfld;
offset = strptr = (char *)NULL;
curfld = 0;
strbuf += ofset;
while (*strbuf) {
strptr = !offset ? strbuf : offset;
offset = strpbrk ((!offset ? strbuf : offset), sep);
if (offset) {
offset++;
} else if (curfld != fldno) {
*retstr = 0;
break;
}
if (curfld == fldno) {
strncpy (retstr, strptr,
(int)(!offset ? strlen (strptr)+ 1 :
(int)(offset - strptr)));
if (offset)
retstr[offset - strptr - 1] = 0;
break;
}
curfld++;
}
return retstr;
}
// Included because strpbrk is not in the arduino gcc/g++ libraries
// Or I just could not find it :)
char * strpbrk (const char *s1, const char *s2)
{
const char *c = s2;
if (!*s1) {
return (char *) NULL;
}
while (*s1) {
for (c = s2; *c; c++) {
if (*s1 == *c)
break;
}
if (*c)
break;
s1++;
}
if (*c == '\0')
s1 = NULL;
return (char *) s1;
}
A light-weight approach (no strict checks on valid parses of the integers and ignoring any list elements past a fixed maximum):
char buf[32] = "COMSTEP 789 665 432 END"; // assume this has just been read
int res[8], nres = 0;
bool inlist = false;
for (char *p = strtok(buf, " "); p; p = strtok(0, " "))
if (inlist)
{
if (!strcmp(p, "END"))
{
inlist = false;
break;
}
else if (nres < sizeof(res) / sizeof(*res))
res[nres++] = atoi(p);
}
else if (!strcmp(p, "COMSTEP"))
inlist = true;
if (!inlist)
for (size_t i = 0; i < nres; ++i)
printf("%d%s", res[i], i + 1 < nres ? " " : "\n"); // do whatever
So I am creating a calculator that will compute numbers.I have this code(I will not include very basic codes)
long num1, num2, answer;
boolean mySwitch = false;
boolean do_subtraction_flag = false; // when true we will apply subtraction
boolean multiply = false;
boolean divide = false;
void loop()
{
char keypressed = myKeypad.getKey();
if(keypressed != NO_KEY)
{
Serial.print(keypressed);
if(keypressed > 47 && keypressed < 58) // is between '0' and '9'
{
if(!mySwitch)
{
num1 = (num1 * 10) + (keypressed - 48);
}
else
{
num2 = (num2 * 10) + (keypressed - 48);
}
}
if(keypressed == '=')
{
if(do_subtraction_flag) // we want to subtract the numbers
{
answer = num1 - num2;
}else if(multiply){
answer = num1 * num2;
}else if(divide){
answer = num1 / num2;
}
else // we want to add the numbers
{
answer = num1 + num2;
}
Serial.println(answer);
num1 = 0;
num2 = 0;
mySwitch = false;
do_subtraction_flag = false;
multiply = false;
divide = false;
}
else if(keypressed == '+')
{
mySwitch = true;
}
else if(keypressed == '*'){
mySwitch = true;
multiply = true;
}else if(keypressed == '/'){
mySwitch = true;
divide = true;
}
else if(keypressed == '-')
{
mySwitch = true;
do_subtraction_flag = true;
}else if (keypressed == 'C'){
for (int i=0; i < 80; i++)
{
Serial.write(8); // print 80 times backspace (BS)
}
}
}
}
Im confused here because I want to compute multiple number with multiple operands (eg., 2+1+3 or 2+1-2), but when i add another variable 'num3' what should I suppose to do with it? if i put it into the do_subtraction flag what if the user inputs 2-1+3?Is it possible to compute 3 numbers with this code?I'm getting confused here but let me know if you are also confused what I want to do
Make an array to hold the numbers and operators that you input, then loop through them when the input == '='. Have your num1 always hold the result and num2 hold the next array value.
There is an issue using QString::arg() when a string contains a digit right after a place marker. It's not clear from the QString::arg() function description what would happen in case of such a replacement:
QString("String for replacement %1234").arg("blah");
Will this result in "String for replacement blah234" or "String for replacement blah34"?
I looked in the QT's source code to answer this question. It seems that the algorithm which looks for place markers is 'greedy' and it would take both digits in the example above.
Here is the source of the QT's function which is used inside the QString::arg() (QT 4.8.4):
static ArgEscapeData findArgEscapes(const QString &s)
{
const QChar *uc_begin = s.unicode();
const QChar *uc_end = uc_begin + s.length();
ArgEscapeData d;
d.min_escape = INT_MAX;
d.occurrences = 0;
d.escape_len = 0;
d.locale_occurrences = 0;
const QChar *c = uc_begin;
while (c != uc_end) {
while (c != uc_end && c->unicode() != '%')
++c;
if (c == uc_end)
break;
const QChar *escape_start = c;
if (++c == uc_end)
break;
bool locale_arg = false;
if (c->unicode() == 'L') {
locale_arg = true;
if (++c == uc_end)
break;
}
if (c->digitValue() == -1)
continue;
int escape = c->digitValue();
++c;
if (c != uc_end && c->digitValue() != -1) {
escape = (10 * escape) + c->digitValue();
++c;
}
if (escape > d.min_escape)
continue;
if (escape < d.min_escape) {
d.min_escape = escape;
d.occurrences = 0;
d.escape_len = 0;
d.locale_occurrences = 0;
}
++d.occurrences;
if (locale_arg)
++d.locale_occurrences;
d.escape_len += c - escape_start;
}
return d;
}
Is there a better way of solving such an ambiguity than always using a 2-digit place markers?
Since you can only use %1 to %99 as markers and you can skip marker numbers you can write:
QString("String for replacement %10234").arg("blah");
to output String for replacement blah234
Qt help states for arg(const QString & a, int fieldWidth = 0, QChar fillChar = QLatin1Char( ' ' ))
Returns a copy of this string with the lowest numbered place marker replaced by string a, i.e., %1, %2, ..., %99.
...
Place marker numbers must be in the range 1 to 99.
Therefore, what you're seeing is, by definition, correct; the first two numbers will be replaced. If you're wanting "String for replacement blah234", then you could define the string as: -
QString("String for replacement %1%2").arg("blah").arg(234);
I have the same issue, but the order answers not looks like a good way for me.
I have resolve the ambiguity in this way.
QString myString= QString("ConcatenatedNumbers%0123").arg(66,3,10, QChar('0'));
The string will be:
ConcatenatedNumbers06623
I want to partition a QByteArray message efficiently, so this function I implemented take the Bytes, the part I want to extract, and toEnd flag which tells if I want to extract part1 till the end of the array. my dilimeter is spcae ' '
example if I have:
ba = "HELLO HOW ARE YOU?"
ba1 = getPart(ba, 1, false) -> ba1 = "HELLO"
ba2 = getPart(ba, 2, true) -> ba2 = "HOW ARE YOU?"
ba3 = getPart(ba, 3, false) -> ba3 = "ARE"
the function below works just fine, but I am wondering if this is efficient. should I consider using split function?
QByteArray Server::getPart(const QByteArray message, int part, bool toEnd)
{
QByteArray string;
int startsFrom = 0;
int endsAt = 0;
int count = 0;
for(int i = 0; i < message.size(); i++)
{
if(message.at(i) == ' ')
{
count++;
if(part == count)
{
endsAt = i;
break;
}
string.clear();
startsFrom = i + 1;
}
string.append(message.at(i));
}
if(toEnd)
{
for(int i = endsAt; i < message.size(); i++)
{
string.append(message.at(i));
}
}
return string;
}
What about this:
QByteArray Server::getPart(const QByteArray& message, int part, bool toEnd)
{
int characters(toEnd ? -1 : message.indexOf(' ', part) - part);
return message.mid(part, characters);
}
Why not make it a regular QString and use split. That will give you a QStringList.