9

I want to solve a conditional equation in iOS:

The equation I get from database is in NSString format, for example:

if((height > 0), (weight+ 2 ), ( weight-1 ) )

As per our understanding, if I parse the above string and separateheight>0condition, it will be in the NSString format. But to evaluate it how do I convert the string to a conditional statement?

Once the conditional statement is obtained the equation can be solved by converting it to a ternary equation as follows:

Bool status;

NSString *condition=@” height>0”;

If(condition)    //But condition is treated as a string and not as a conditional statement.
{
    status=True;
}
else
{
    status=False;
}

Return status ? weight+ 2 : weight-1;`

Also the equations can dynamically change, so they cannot be hard coded. In short how do I solve this equation which I get as a NSString.

Thank you for your patience!

Linus Caldwell
  • 10,908
  • 12
  • 46
  • 58
Sagar Jadhav
  • 249
  • 1
  • 7
  • Are you working with live data in existing format, or you can use any format you want? – tia May 16 '13 at 10:13
  • related to http://stackoverflow.com/questions/2550897/eval-for-objective-c-code – Daniel May 16 '13 at 10:14
  • In any ways `height` and `weight` will not be the variable to to compare. – rptwsthi May 16 '13 at 10:15
  • Hey Tia, we get equations from the webserver which can be updated. However they will be in the same format. A few equations that we get are as follows: Eg 1. if((height > 0), ( weight / ( height / 100 ) ^ 2 ), ( -1 ) ) – Sagar Jadhav May 16 '13 at 10:25
  • What are the values of the variables in the equation? How do you get those? – Amar May 16 '13 at 10:26
  • use componentseperatedbystring:@">". get last object.make it int. then compare – shivam May 16 '13 at 10:42
  • @Amar : these are th float values stored in database – Sagar Jadhav May 16 '13 at 10:43
  • You either need to write an interpreter or "steal" one from somewhere. I advise theft, since writing your own is quite difficult and full of pitfalls. Parsing is only the start -- maybe 10-15% of the job. – Hot Licks May 21 '13 at 15:32

2 Answers2

5

DDMathParser author here...

To expand on Jonathan's answer, here's how you could do it entirely in DDMathParser. However, to parse the string as-is, you'll need to do two things.

First, you'll need to create an if function:

DDMathEvaluator *evaluator = [DDMathEvaluator sharedMathEvaluator];
[evaluator registerFunction:^DDExpression *(NSArray *args, NSDictionary *vars, DDMathEvaluator *eval, NSError *__autoreleasing *error) {
    if ([args count] == 3) {
        DDExpression *condition = [args objectAtIndex:0];
        DDExpression *resultExpression = nil;
        NSNumber *conditionValue = [condition evaluateWithSubstitutions:vars evaluator:eval error:error];
        if ([conditionValue boolValue] == YES) {
            resultExpression = [args objectAtIndex:1];
        } else {
            resultExpression = [args objectAtIndex:2];
        }
        NSNumber *result = [resultExpression evaluateWithSubstitutions:vars evaluator:eval error:error];
        return [DDExpression numberExpressionWithNumber:result];
    }
    return nil;

} forName:@"if"];

This creates the if() function, which takes three parameters. Depending on how the first parameter evaluates, it either evaluates to the result of the second or third parameter.

The other thing you'll need to do is tell the evaluator what height and weight mean. Since they don't start with a $ character, they get interpreted as functions, and not variables. If they started with a $, then it would be as simple as evaluating it like this:

NSString *expression = @"if(($height > 0), ($weight+ 2 ), ( $weight-1 ) )";
NSDictionary *variables = @{@"height" : @42, @"weight" : @13};
NSNumber *result = [expression evaluateWithSubstitutions:variables evaluator:evaluator error:nil];

However, since they don't start with a $, they're functions, which means you need to tell the evaluator what the functions evaluate to. You could do this by creating functions for both height and weight, just like you did for if:

[evaluator registerFunction:^DDExpression *(NSArray *args, NSDictionary *vars, DDMathEvaluator *eval, NSError **error) {
    return [DDExpression numberExpressionWithNumber:@42];
} forName:@"height"];

Alternatively, you could make it a bit more dynamic and use the functionResolver block of DDMathEvaluator, which is a block that returns a block (woooooo) and would look like this:

NSDictionary *values = @{@"height": @42, @"weight": @13};
[evaluator setFunctionResolver:^DDMathFunction(NSString *name) {
    DDMathFunction f = ^(NSArray *args, NSDictionary *vars, DDMathEvaluator *eval, NSError **error) {
        NSNumber *n = [values objectForKey:name];
        if (!n) { n = @0; }
        return [DDExpression numberExpressionWithNumber:n];
    };
    return f;
}];

With those two pieces in place (registering if and providing the values of height and weight), you can do:

NSString *expression = @"if((height > 0), (weight+ 2 ), ( weight-1 ) )";
NSNumber *result = [expression evaluateWithSubstitutions:nil evaluator:evaluator error:nil];

... and get back the proper result of @15.

(I have plans to make DDMathParser allow unknown functions to fall back to provided variable values, but I haven't quite finished it yet)

Community
  • 1
  • 1
Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
  • I like the start of the answer! :-) – Amar May 17 '13 at 05:52
  • How to approach in case an equation has a function call Ex: `if(xyz==0, if(abc<0, 270, 90), atan(xyz/abc)*180/_pi)`? – Amar May 17 '13 at 06:07
  • @Amar as long as you define what `_pi`, `xyz`, and `abc` all are, that'll work just fine. `atan` is a built-in function. (But if you called it `pi` instead of `_pi`, then you wouldn't need to define it. [`pi` is also a built-in function](https://github.com/davedelong/DDMathParser/wiki/Built-in-Functions). – Dave DeLong May 17 '13 at 06:09
  • I am getting error No visible interface for DDMathEvaluator declares the selector evaluateExpression:withSubstitutions. I looked in DDMathEvaluator.h, no expression like "evaluateExpression:withSubstitutions". – Sagar Jadhav May 20 '13 at 09:18
  • @SagarJadhav whoops, sorry about that. That's code I haven't pushed yet. I updated the answer. – Dave DeLong May 20 '13 at 16:46
  • Sorry but when NSString *expression = @"if((height > 0), (weight+ 2 ), ( weight-1 ) )"; NSNumber *result = [expression numberByEvaluatingString]; in above equation numberbyEvaluatingString not found error – Sagar Jadhav May 21 '13 at 08:21
  • @SagarJadhav same issue as before... :P – Dave DeLong May 21 '13 at 14:54
  • @ Dave DeLong: Hi I am doing as given below, still I am getting zero output. So please let me know where I am doing wrong. NSString *expression = @"if((height > 0), (weight+ 2 ), ( weight-1 ) )"; DDExpression *resultExpression = [DDExpression variableExpressionWithVariable:expression]; NSDictionary *variables = @{@"height" : @42, @"weight" : @13}; NSNumber *result = [resultExpression evaluateWithSubstitutions:variables evaluator:evaluator error:nil]; – Sagar Jadhav May 27 '13 at 05:44
  • @ Dave DeLong: Hi Dave first of all thanks for your imediate response. Can You tell me how to solve this equation: @"sqrt(x^2 + y^2 )"; – Sagar Jadhav May 27 '13 at 07:17
  • @SagarJadhav your first one is wrong; don't create a variableExpression; do what I said in the answer. As for your second one, `^` means [bitwise-XOR](http://en.wikipedia.org/wiki/XOR), not raise-to-the-power-of. To do a power operation, use `**`. This is all explained [on the wiki](https://github.com/davedelong/DDMathParser/wiki/Operators). – Dave DeLong May 27 '13 at 16:59
  • @SagarJadhav If this answer was helpful to you, pls accept it and give the author the credit he deserves! – Amar Jun 19 '13 at 12:10
4

you will have to write your own interpreter or find one that supports this kind of expressions.

The first part (the condition) can be evaluated by NSPredicate. For the second part (the calculation) you will need some math expression evaluation. Try this out https://github.com/davedelong/DDMathParser. Maybe you can do both with DDMathParser but i am not sure.

Jonathan Cichon
  • 4,396
  • 16
  • 19