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


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

NSPredicate *p = [NSComparisonPredicate 

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],
NSExpression *eA = [NSExpression 
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;

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

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
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.