Sunday, March 13, 2011

NSExpression: simple examples

The example from last time (here) introduced the NSExpression class. That one was rather complex in code, though what it does is just a simple filtering of values.

Here is another example which looks less forbidding: it combines two expressions in a single predicate. These can then be used to construct a comparison predicate that grabs the object for key='value' from an array, and checks it against the number 10, like this:

As before the code block is inside a standard main:


#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
code here..    
[pool drain];
return 0;
}


and compiled like this:


> gcc -o test test.m -framework Foundation


code:


NSExpression *lhs = [NSExpression expressionForKeyPath:@"value"];
NSNumber *ten = [NSNumber numberWithInt:10];
NSExpression *rhs = [NSExpression expressionForConstantValue:ten];

NSPredicate *p = [NSComparisonPredicate 
predicateWithLeftExpression:lhs
rightExpression:rhs
modifier:NSDirectPredicateModifier 
type:NSGreaterThanOrEqualToPredicateOperatorType 
options:0];

NSArray *A = [NSArray arrayWithObjects:
[NSMutableDictionary dictionaryWithObject:
[NSNumber numberWithInt:3] forKey:@"value"],
[NSMutableDictionary dictionaryWithObject:
[NSNumber numberWithInt:15] forKey:@"value"], nil];
NSArray *fA = [A filteredArrayUsingPredicate:p];
for (id obj in fA) {
NSLog(@"%@", [obj description]);
}



> ./test
2011-03-13 10:58:53.885 test[218:903] {
value = 15;
}


Here is a second example, taken from the docs, of how to construct an expression that uses a built-in function (they call this a function expression):


NSArray *A = [NSArray arrayWithObjects:
[NSNumber numberWithInt:3],
[NSNumber numberWithInt:6],
nil];
NSExpression *eA = [NSExpression 
expressionForConstantValue:A];
NSArray *args = [NSArray arrayWithObject:eA];

NSExpression *e = [NSExpression 
expressionForFunction:@"average:" arguments:args];
id result = [e expressionValueWithObject:nil context:nil];
NSLog(@"%@ %@", [result description], [result class]);
float f = [result floatValue];
printf("result = %3.2f\n", f);



2011-03-13 11:01:00.337 test[236:903] 4.5 NSCFNumber
result = 4.50


There are lots of built-in functions available (here).

The third example is based on the first part (the simple part) of this post from Dave DeLong. It defines a category on NSNumber


@interface NSNumber (FactorialExpression)
- (NSNumber *) factorial;
@end

@implementation NSNumber (FactorialExpression)
- (NSNumber *) factorial {
double baseValue = [self doubleValue];
double result = tgamma(baseValue+1);
return [NSNumber numberWithDouble:result];
}
@end


and the code block is:


NSNumber *n = [NSNumber numberWithDouble:4.2];
NSLog(@"%@ %@", n, [n factorial]);
NSLog(@"%p %d", n, [n respondsToSelector:@selector(factorial)]);

NSExpression *f = [NSExpression expressionForConstantValue:n];
NSExpression *e = [NSExpression expressionForFunction:f
selectorName:@"factorial" 
arguments:nil];
NSLog(@"operand %@ %@", [e operand], [[e operand] class]);
NSLog(@"operand %@", [e function]);

id result = [e expressionValueWithObject:nil context:nil];
NSLog(@"%@ %@", [result description], [result class]);



> ./test
2011-03-13 11:02:46.798 test[251:903] 4.2 32.57809605033135
2011-03-13 11:02:46.800 test[251:903] 0x100108d20 1
2011-03-13 11:02:46.801 test[251:903] operand 4.2 NSConstantValueExpression
2011-03-13 11:02:46.801 test[251:903] operand factorial
2011-03-13 11:02:46.802 test[251:903] 32.57809605033135 NSCFNumber


And now, the way to solve my initial question seems clear:

Define a category on NSString that does what I want.
Wrap the call up in an NSExpression and then an NSPredicate. Next time, if I succeed.