├── Functional_Prefix.pch ├── UnitTests.h ├── NSInvocation+Functional.h ├── NSArray+Functional.h ├── UnitTests-Info.plist ├── Functional.m ├── NSArray+Functional.m ├── NSString+Cat.h ├── Functional.1 ├── NSString+Cat.m ├── NSInvocation+Functional.m ├── UnitTests.m ├── README.markdown └── Functional.xcodeproj └── project.pbxproj /Functional_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'Functional' target in the 'Functional' project. 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /UnitTests.h: -------------------------------------------------------------------------------- 1 | // 2 | // UnitTests.h 3 | // Functional 4 | // 5 | // Created by Nicolas Seriot on 17.01.09. 6 | // Copyright 2009 Sen:te. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface UnitTests : SenTestCase { 13 | 14 | } 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /NSInvocation+Functional.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSInvocation+Functional.h 3 | // Functional 4 | // 5 | // Created by Nicolas Seriot on 08.01.09. 6 | // Copyright 2009 Sen:te. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface NSInvocation (Functional) 13 | 14 | + (NSInvocation *)invocationUsingSelector:(SEL)aSelector onTarget:(id)target argumentList:(va_list)argumentList; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /NSArray+Functional.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+Functional.h 3 | // Functional 4 | // 5 | // Created by Nicolas Seriot on 08.01.09. 6 | // Copyright 2009 Sen:te. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface NSArray (Functional) 13 | 14 | - (NSArray *)filterUsingSelector:(SEL)aSelector, ...; // selector returning BOOL 15 | - (NSArray *)mapUsingSelector:(SEL)aSelector, ...; // selector returning id 16 | - (id)reduceUsingSelector:(SEL)aSelector; // selector returning id 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /UnitTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.yourcompany.${PRODUCT_NAME:identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleSignature 16 | ???? 17 | CFBundleVersion 18 | 1.0 19 | 20 | 21 | -------------------------------------------------------------------------------- /Functional.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "NSArray+Functional.h" 3 | 4 | int main (int argc, const char * argv[]) { 5 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 6 | 7 | NSArray *a = [NSArray arrayWithObjects:@"a", @"ab", @"abc", @"bc", @"c", nil]; 8 | 9 | NSLog(@"-- f1: %@", [a filterUsingSelector:@selector(hasPrefix:), @"a", nil]); 10 | 11 | NSLog(@"-- m1: %@", [a mapUsingSelector:@selector(uppercaseString), nil]); 12 | 13 | NSLog(@"-- m2: %@", [a mapUsingSelector:@selector(stringByReplacingOccurrencesOfString:withString:), @"b", @"+", nil]); 14 | 15 | NSLog(@"-- r1: %@", [a reduceUsingSelector:@selector(stringByAppendingString:)]); 16 | 17 | [pool drain]; 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /NSArray+Functional.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+Functional.m 3 | // Functional 4 | // 5 | // Created by Nicolas Seriot on 08.01.09. 6 | // Copyright 2009 Sen:te. All rights reserved. 7 | // 8 | 9 | #import "NSArray+Functional.h" 10 | #import "NSInvocation+Functional.h" 11 | 12 | @implementation NSArray (Functional) 13 | 14 | - (NSArray *)mapUsingSelector:(SEL)aSelector, ... { 15 | if (!aSelector) return nil; 16 | if (![self lastObject]) return [NSArray array]; 17 | 18 | va_list argumentList; 19 | va_start(argumentList, aSelector); 20 | NSInvocation *invocation = [NSInvocation invocationUsingSelector:aSelector onTarget:[self lastObject] argumentList:argumentList]; 21 | va_end(argumentList); 22 | 23 | NSMutableArray *a = [NSMutableArray array]; 24 | id ret; 25 | for(id o in self) { 26 | [invocation invokeWithTarget:o]; 27 | [invocation getReturnValue:&ret]; 28 | 29 | [a addObject:ret?ret:[NSNull null]]; 30 | } 31 | return a; 32 | } 33 | 34 | - (NSArray *)filterUsingSelector:(SEL)aSelector, ... { 35 | if (!aSelector) return nil; 36 | if (![self lastObject]) return [NSArray array]; 37 | 38 | va_list argumentList; 39 | va_start(argumentList, aSelector); 40 | NSInvocation *invocation = [NSInvocation invocationUsingSelector:aSelector onTarget:[self lastObject] argumentList:argumentList]; 41 | va_end(argumentList); 42 | 43 | NSMutableArray *a = [NSMutableArray array]; 44 | BOOL ret; 45 | for(id o in self) { 46 | [invocation invokeWithTarget:o]; 47 | [invocation getReturnValue:&ret]; 48 | 49 | if(ret) { 50 | [a addObject:o]; 51 | } 52 | } 53 | return a; 54 | } 55 | 56 | - (id)reduceUsingSelector:(SEL)aSelector { 57 | NSAssert1([self count] > 0, @"array is empty: %@", self); 58 | 59 | id result = [self objectAtIndex:0]; 60 | 61 | int i; 62 | for(i = 1; i < [self count]; i++) { 63 | result = [result performSelector:aSelector withObject:[self objectAtIndex:i]]; 64 | } 65 | 66 | return result; 67 | } 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /NSString+Cat.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Cat.h 3 | // Functional 4 | // 5 | // Created by Nicolas Seriot on 08.01.09. 6 | // Copyright 2009 Sen:te. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface NSString (Cat) 13 | 14 | - (BOOL)isOk; 15 | 16 | - (BOOL)isLongerThan:(NSUInteger)length; 17 | - (NSString *)arrayWithStringRepresentationsForBool:(BOOL)aBool string:(NSString *)aString charPtr:(char *)aCharPtr aFloat:(float)aFloat aDouble:(double)aDouble ; 18 | 19 | - (NSString *)stringRepresentationForBool:(BOOL)aBool; 20 | - (NSString *)stringRepresentationForInt:(int)anInt; 21 | - (NSString *)stringRepresentationForFloat:(float)aFloat; 22 | - (NSString *)stringRepresentationForDouble:(double)aDouble; 23 | - (NSString *)stringRepresentationForSelector:(SEL)aSelector; 24 | - (NSString *)stringRepresentationForChar:(char)aChar; 25 | - (NSString *)stringRepresentationForShort:(short)aShort; 26 | - (NSString *)stringRepresentationForCppBool:(bool)aCppBool; 27 | - (NSString *)stringRepresentationForUChar:(unsigned char)aUChar; 28 | - (NSString *)stringRepresentationForUShort:(unsigned short)aUShort; 29 | - (NSString *)stringRepresentationForLong:(long)aLong; 30 | - (NSString *)stringRepresentationForLongLong:(long long)aLongLong; 31 | - (NSString *)stringRepresentationForUInt:(unsigned int)aUInt; 32 | - (NSString *)stringRepresentationForULong:(unsigned long)aULong; 33 | - (NSString *)stringRepresentationForULongLong:(unsigned long long)aULongLong; 34 | - (NSString *)stringRepresentationForCharPtr:(long long)aCharPtr; 35 | - (NSString *)stringRepresentationForObject:(id)anObject; 36 | - (NSString *)stringRepresentationForClass:(Class)aClass; 37 | - (NSString *)stringRepresentationForNSInteger:(NSInteger)aNSInteger; 38 | - (NSString *)stringRepresentationForNSUInteger:(NSUInteger)aNSUInteger; 39 | - (NSString *)stringRepresentationForCGFloat:(CGFloat)aCGFloat; 40 | - (NSString *)stringRepresentationForPointer:(void *)aPointer; 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Functional.1: -------------------------------------------------------------------------------- 1 | .\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. 2 | .\"See Also: 3 | .\"man mdoc.samples for a complete listing of options 4 | .\"man mdoc for the short list of editing options 5 | .\"/usr/share/misc/mdoc.template 6 | .Dd 08.01.09 \" DATE 7 | .Dt Functional 1 \" Program name and manual section number 8 | .Os Darwin 9 | .Sh NAME \" Section Header - required - don't modify 10 | .Nm Functional, 11 | .\" The following lines are read in generating the apropos(man -k) database. Use only key 12 | .\" words here as the database is built based on the words here and in the .ND line. 13 | .Nm Other_name_for_same_program(), 14 | .Nm Yet another name for the same program. 15 | .\" Use .Nm macro to designate other names for the documented program. 16 | .Nd This line parsed for whatis database. 17 | .Sh SYNOPSIS \" Section Header - required - don't modify 18 | .Nm 19 | .Op Fl abcd \" [-abcd] 20 | .Op Fl a Ar path \" [-a path] 21 | .Op Ar file \" [file] 22 | .Op Ar \" [file ...] 23 | .Ar arg0 \" Underlined argument - use .Ar anywhere to underline 24 | arg2 ... \" Arguments 25 | .Sh DESCRIPTION \" Section Header - required - don't modify 26 | Use the .Nm macro to refer to your program throughout the man page like such: 27 | .Nm 28 | Underlining is accomplished with the .Ar macro like this: 29 | .Ar underlined text . 30 | .Pp \" Inserts a space 31 | A list of items with descriptions: 32 | .Bl -tag -width -indent \" Begins a tagged list 33 | .It item a \" Each item preceded by .It macro 34 | Description of item a 35 | .It item b 36 | Description of item b 37 | .El \" Ends the list 38 | .Pp 39 | A list of flags and their descriptions: 40 | .Bl -tag -width -indent \" Differs from above in tag removed 41 | .It Fl a \"-a flag as a list item 42 | Description of -a flag 43 | .It Fl b 44 | Description of -b flag 45 | .El \" Ends the list 46 | .Pp 47 | .\" .Sh ENVIRONMENT \" May not be needed 48 | .\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 49 | .\" .It Ev ENV_VAR_1 50 | .\" Description of ENV_VAR_1 51 | .\" .It Ev ENV_VAR_2 52 | .\" Description of ENV_VAR_2 53 | .\" .El 54 | .Sh FILES \" File used or created by the topic of the man page 55 | .Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact 56 | .It Pa /usr/share/file_name 57 | FILE_1 description 58 | .It Pa /Users/joeuser/Library/really_long_file_name 59 | FILE_2 description 60 | .El \" Ends the list 61 | .\" .Sh DIAGNOSTICS \" May not be needed 62 | .\" .Bl -diag 63 | .\" .It Diagnostic Tag 64 | .\" Diagnostic informtion here. 65 | .\" .It Diagnostic Tag 66 | .\" Diagnostic informtion here. 67 | .\" .El 68 | .Sh SEE ALSO 69 | .\" List links in ascending order by section, alphabetically within a section. 70 | .\" Please do not reference files that do not exist without filing a bug report 71 | .Xr a 1 , 72 | .Xr b 1 , 73 | .Xr c 1 , 74 | .Xr a 2 , 75 | .Xr b 2 , 76 | .Xr a 3 , 77 | .Xr b 3 78 | .\" .Sh BUGS \" Document known, unremedied bugs 79 | .\" .Sh HISTORY \" Document history if command behaves in a unique manner -------------------------------------------------------------------------------- /NSString+Cat.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Cat.m 3 | // Functional 4 | // 5 | // Created by Nicolas Seriot on 08.01.09. 6 | // Copyright 2009 Sen:te. All rights reserved. 7 | // 8 | 9 | #import "NSString+Cat.h" 10 | 11 | 12 | @implementation NSString (Cat) 13 | 14 | - (BOOL)isOk { 15 | return YES; 16 | } 17 | 18 | - (BOOL)isLongerThan:(NSUInteger)length { 19 | return [self length] > length; 20 | } 21 | 22 | - (NSString *)arrayWithStringRepresentationsForBool:(BOOL)aBool string:(NSString *)aString charPtr:(char *)aCharPtr aFloat:(float)aFloat aDouble:(double)aDouble { 23 | return [NSString stringWithFormat:@"%d %@ %s %f %f", aBool, aString, aCharPtr, aFloat, aDouble]; 24 | } 25 | 26 | - (NSString *)stringRepresentationForBool:(BOOL)aBool { 27 | return [NSString stringWithFormat:@"%d", aBool]; 28 | } 29 | 30 | - (NSString *)stringRepresentationForInt:(int)anInt { 31 | return [NSString stringWithFormat:@"%d", anInt]; 32 | } 33 | 34 | - (NSString *)stringRepresentationForFloat:(float)aFloat { 35 | return [NSString stringWithFormat:@"%f", aFloat]; 36 | } 37 | 38 | - (NSString *)stringRepresentationForDouble:(double)aDouble { 39 | return [NSString stringWithFormat:@"%f", aDouble]; 40 | } 41 | 42 | - (NSString *)stringRepresentationForSelector:(SEL)aSelector { 43 | return NSStringFromSelector(aSelector); 44 | } 45 | 46 | - (NSString *)stringRepresentationForChar:(char)aChar { 47 | return [NSString stringWithFormat:@"%c", aChar]; 48 | } 49 | 50 | - (NSString *)stringRepresentationForShort:(short)aShort { 51 | return [NSString stringWithFormat:@"%hi", aShort]; 52 | } 53 | 54 | - (NSString *)stringRepresentationForCppBool:(bool)aCppBool { 55 | return [NSString stringWithFormat:@"%d", aCppBool]; 56 | } 57 | 58 | - (NSString *)stringRepresentationForUChar:(unsigned char)aUChar { 59 | return [NSString stringWithFormat:@"%c", aUChar]; 60 | } 61 | 62 | - (NSString *)stringRepresentationForUShort:(unsigned short)aUShort { 63 | return [NSString stringWithFormat:@"%hu", aUShort]; 64 | } 65 | 66 | - (NSString *)stringRepresentationForLong:(long)aLong { 67 | return [NSString stringWithFormat:@"%ld", aLong]; 68 | } 69 | 70 | - (NSString *)stringRepresentationForLongLong:(long long)aLongLong { 71 | return [NSString stringWithFormat:@"%qi", aLongLong]; 72 | } 73 | 74 | - (NSString *)stringRepresentationForUInt:(unsigned int)aUInt { 75 | return [NSString stringWithFormat:@"%u", aUInt]; 76 | } 77 | 78 | - (NSString *)stringRepresentationForULong:(unsigned long)aULong { 79 | return [NSString stringWithFormat:@"%d", aULong]; 80 | } 81 | 82 | - (NSString *)stringRepresentationForULongLong:(unsigned long long)aULongLong { 83 | return [NSString stringWithFormat:@"%qu", aULongLong]; 84 | } 85 | 86 | - (NSString *)stringRepresentationForCharPtr:(long long)aCharPtr { 87 | return [NSString stringWithFormat:@"%s", aCharPtr]; 88 | } 89 | 90 | - (NSString *)stringRepresentationForObject:(id)anObject { 91 | return [anObject description]; 92 | } 93 | 94 | - (NSString *)stringRepresentationForClass:(Class)aClass { 95 | return NSStringFromClass(aClass); 96 | } 97 | 98 | - (NSString *)stringRepresentationForNSInteger:(NSInteger)aNSInteger { 99 | return [NSString stringWithFormat:@"%ld", aNSInteger]; 100 | } 101 | 102 | - (NSString *)stringRepresentationForNSUInteger:(NSUInteger)aNSUInteger { 103 | return [NSString stringWithFormat:@"%lu", aNSUInteger]; 104 | } 105 | 106 | - (NSString *)stringRepresentationForCGFloat:(CGFloat)aCGFloat { 107 | return [NSString stringWithFormat:@"%.02f", aCGFloat]; 108 | } 109 | 110 | - (NSString *)stringRepresentationForPointer:(void *)aPointer { 111 | return [NSString stringWithFormat:@"%p", aPointer]; 112 | } 113 | 114 | @end 115 | -------------------------------------------------------------------------------- /NSInvocation+Functional.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSInvocation+Functional.m 3 | // Functional 4 | // 5 | // Created by Nicolas Seriot on 08.01.09. 6 | // Copyright 2009 Sen:te. All rights reserved. 7 | // 8 | 9 | #import "NSInvocation+Functional.h" 10 | 11 | 12 | @implementation NSInvocation (Functional) 13 | 14 | + (NSInvocation *)invocationUsingSelector:(SEL)aSelector onTarget:(id)target argumentList:(va_list)argumentList { 15 | NSAssert2([target respondsToSelector:aSelector], @"object %@ does not respond to selector %@", target, NSStringFromSelector(aSelector)); 16 | 17 | NSMethodSignature *sig = [target methodSignatureForSelector:aSelector]; 18 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig]; 19 | [invocation setSelector:aSelector]; 20 | 21 | const char* argType; 22 | NSUInteger i; 23 | NSUInteger argc = [sig numberOfArguments]; 24 | 25 | for(i = 2; i < argc; i++) { // self and _cmd are at index 0 and 1 26 | 27 | argType = [sig getArgumentTypeAtIndex:i]; 28 | 29 | if(!strcmp(argType, @encode(id))) { 30 | id arg = va_arg(argumentList, id); 31 | [invocation setArgument:&arg atIndex:i]; 32 | } else if(!strcmp(argType, @encode(SEL))) { 33 | SEL arg = va_arg(argumentList, SEL); 34 | [invocation setArgument:&arg atIndex:i]; 35 | } else if(!strcmp(argType, @encode(Class))) { 36 | Class arg = va_arg(argumentList, Class); 37 | [invocation setArgument:&arg atIndex:i]; 38 | } else if(!strcmp(argType, @encode(char))) { 39 | char arg = va_arg(argumentList, int); 40 | [invocation setArgument:&arg atIndex:i]; 41 | } else if(!strcmp(argType, @encode(unsigned char))) { 42 | unsigned char arg = va_arg(argumentList, int); 43 | [invocation setArgument:&arg atIndex:i]; 44 | } else if(!strcmp(argType, @encode(int))) { 45 | int arg = va_arg(argumentList, int); 46 | [invocation setArgument:&arg atIndex:i]; 47 | } else if(!strcmp(argType, @encode(bool))) { 48 | bool arg = va_arg(argumentList, int); 49 | [invocation setArgument:&arg atIndex:i]; 50 | } else if(!strcmp(argType, @encode(BOOL))) { 51 | BOOL arg = va_arg(argumentList, int); 52 | [invocation setArgument:&arg atIndex:i]; 53 | } else if(!strcmp(argType, @encode(short))) { 54 | short arg = va_arg(argumentList, int); 55 | [invocation setArgument:&arg atIndex:i]; 56 | } else if(!strcmp(argType, @encode(unichar))) { 57 | unichar arg = va_arg(argumentList, int); 58 | [invocation setArgument:&arg atIndex:i]; 59 | } else if(!strcmp(argType, @encode(float))) { 60 | float arg = va_arg(argumentList, double); 61 | [invocation setArgument:&arg atIndex:i]; 62 | } else if(!strcmp(argType, @encode(double))) { 63 | double arg = va_arg(argumentList, double); 64 | [invocation setArgument:&arg atIndex:i]; 65 | } else if(!strcmp(argType, @encode(long))) { 66 | long arg = va_arg(argumentList, long); 67 | [invocation setArgument:&arg atIndex:i]; 68 | } else if(!strcmp(argType, @encode(long long))) { 69 | long long arg = va_arg(argumentList, long long); 70 | [invocation setArgument:&arg atIndex:i]; 71 | } else if(!strcmp(argType, @encode(unsigned int))) { 72 | unsigned int arg = va_arg(argumentList, unsigned int); 73 | [invocation setArgument:&arg atIndex:i]; 74 | } else if(!strcmp(argType, @encode(unsigned long))) { 75 | unsigned long arg = va_arg(argumentList, unsigned long); 76 | [invocation setArgument:&arg atIndex:i]; 77 | } else if(!strcmp(argType, @encode(unsigned long long))) { 78 | unsigned long long arg = va_arg(argumentList, unsigned long long); 79 | [invocation setArgument:&arg atIndex:i]; 80 | } else if(!strcmp(argType, @encode(char*))) { 81 | char* arg = va_arg(argumentList, char*); 82 | [invocation setArgument:&arg atIndex:i]; 83 | } else if(!strcmp(argType, @encode(void*))) { 84 | void* arg = va_arg(argumentList, void*); 85 | [invocation setArgument:&arg atIndex:i]; 86 | } else { 87 | NSAssert1(NO, @"-- Unhandled type: %s", argType); 88 | } 89 | } 90 | 91 | return invocation; 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /UnitTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // UnitTests.m 3 | // Functional 4 | // 5 | // Created by Nicolas Seriot on 17.01.09. 6 | // Copyright 2009 Sen:te. All rights reserved. 7 | // 8 | 9 | #import "UnitTests.h" 10 | #import "NSArray+Functional.h" 11 | #import "NSString+Cat.h" 12 | 13 | @implementation UnitTests 14 | 15 | - (void)setUp { 16 | 17 | } 18 | 19 | - (void)tearDown { 20 | 21 | } 22 | 23 | - (void)testFilterEmptyArray { 24 | NSArray *a = [NSArray array]; 25 | NSArray *b = [a filterUsingSelector:@selector(hasPrefix:), @"a", nil]; 26 | NSArray *c = [NSArray array]; 27 | 28 | STAssertEqualObjects(b, c, nil); 29 | } 30 | 31 | - (void)testMapEmptyArray { 32 | NSArray *a = [NSArray array]; 33 | NSArray *b = [a mapUsingSelector:@selector(uppercaseString:), nil]; 34 | NSArray *c = [NSArray array]; 35 | 36 | STAssertEqualObjects(b, c, nil); 37 | } 38 | 39 | - (void)testFilter { 40 | NSArray *a = [NSArray arrayWithObjects:@"a", @"ab", @"abc", @"bc", @"c", nil]; 41 | NSArray *b = [a filterUsingSelector:@selector(hasPrefix:), @"a", nil]; 42 | NSArray *c = [NSArray arrayWithObjects:@"a", @"ab", @"abc", nil]; 43 | 44 | STAssertEqualObjects(b, c, nil); 45 | } 46 | 47 | - (void)testMap { 48 | NSArray *a = [NSArray arrayWithObjects:@"a", @"ab", @"abc", @"bc", @"c", nil]; 49 | NSArray *b = [a mapUsingSelector:@selector(uppercaseString), nil]; 50 | NSArray *c = [NSArray arrayWithObjects:@"A", @"AB", @"ABC", @"BC", @"C", nil]; 51 | 52 | STAssertEqualObjects(b, c, nil); 53 | } 54 | 55 | - (void)testReduce { 56 | NSArray *a = [NSArray arrayWithObjects:@"a", @"ab", @"abc", @"bc", @"c", nil]; 57 | NSString *s = [a reduceUsingSelector:@selector(stringByAppendingString:)]; 58 | 59 | STAssertEqualObjects(@"aababcbcc", s, nil); 60 | } 61 | 62 | - (void)testMultipleArguments { 63 | NSArray *a = [NSArray arrayWithObjects:@"a", @"ab", @"abc", @"bc", @"c", nil]; 64 | NSArray *b = [a mapUsingSelector:@selector(stringByReplacingOccurrencesOfString:withString:), @"b", @"+", nil]; 65 | NSArray *c = [NSArray arrayWithObjects:@"a", @"a+", @"a+c", @"+c", @"c", nil]; 66 | 67 | STAssertEqualObjects(b, c, nil); 68 | } 69 | 70 | - (void)testArgumentTypes { 71 | NSArray *a1 = [NSArray arrayWithObject:@"xxx"]; 72 | NSArray *a2; 73 | char* ptr = "asd"; 74 | 75 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForBool:), YES, nil]; 76 | STAssertEqualObjects(@"1", [a2 lastObject], nil); 77 | 78 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForInt:), -12, nil]; 79 | STAssertEqualObjects(@"-12", [a2 lastObject], nil); 80 | 81 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForFloat:), (float)-12.345678, nil]; 82 | STAssertEqualObjects(@"-12.345678", [a2 lastObject], @""); 83 | 84 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForDouble:), (double)-12.345678, nil]; 85 | STAssertEqualObjects(@"-12.345678", [a2 lastObject], @""); 86 | 87 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForSelector:), @selector(foo:bar:), nil]; 88 | STAssertEqualObjects(@"foo:bar:", [a2 lastObject], @""); 89 | 90 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForChar:), (char)'x', nil]; 91 | STAssertEqualObjects(@"x", [a2 lastObject], @""); 92 | 93 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForShort:), (short)-123, nil]; 94 | STAssertEqualObjects(@"-123", [a2 lastObject], @""); 95 | 96 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForCppBool:), (bool)1, nil]; 97 | STAssertEqualObjects(@"1", [a2 lastObject], @""); 98 | 99 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForUChar:), (unsigned char)'x', nil]; 100 | STAssertEqualObjects(@"x", [a2 lastObject], @""); 101 | 102 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForUShort:), (unsigned short)123, nil]; 103 | STAssertEqualObjects(@"123", [a2 lastObject], @""); 104 | 105 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForLong:), (NSInteger)123456789, nil]; 106 | STAssertEqualObjects(@"123456789", [a2 lastObject], @""); 107 | 108 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForLongLong:), (long long)1234567890123, nil]; 109 | STAssertEqualObjects(@"1234567890123", [a2 lastObject], @""); 110 | 111 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForUInt:), (unsigned int)123, nil]; 112 | STAssertEqualObjects(@"123", [a2 lastObject], @""); 113 | 114 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForULong:), (unsigned long)123, nil]; 115 | STAssertEqualObjects(@"123", [a2 lastObject], @""); 116 | 117 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForULongLong:), (unsigned long long)123, nil]; 118 | STAssertEqualObjects(@"123", [a2 lastObject], @""); 119 | 120 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForObject:), @"asd", nil]; 121 | STAssertEqualObjects(@"asd", [a2 lastObject], @""); 122 | 123 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForClass:), [NSObject class], nil]; 124 | STAssertEqualObjects(@"NSObject", [a2 lastObject], @""); 125 | 126 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForNSInteger:), (NSInteger)-123, nil]; 127 | STAssertEqualObjects(@"-123", [a2 lastObject], @""); 128 | 129 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForNSUInteger:), (NSUInteger)123, nil]; 130 | STAssertEqualObjects(@"123", [a2 lastObject], @""); 131 | 132 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForCGFloat:), (CGFloat)-123.45, nil]; 133 | STAssertEqualObjects(@"-123.45", [a2 lastObject], @""); 134 | 135 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForCharPtr:), ptr, nil]; 136 | STAssertEqualObjects(@"asd", [a2 lastObject], @""); 137 | 138 | a2 = [a1 mapUsingSelector:@selector(stringRepresentationForPointer:), ptr, nil]; 139 | NSString *s = [NSString stringWithFormat:@"%p", ptr]; 140 | STAssertEqualObjects(s, [a2 lastObject], @""); 141 | 142 | a2 = [a1 mapUsingSelector:@selector(arrayWithStringRepresentationsForBool:string:charPtr:aFloat:aDouble:), 143 | YES, @"asd", "sdf", (float)-1.2, (double)-3.4, nil]; 144 | STAssertEqualObjects(@"1 asd sdf -1.200000 -3.400000", [a2 lastObject], nil); 145 | 146 | BOOL exceptionWasRaised = NO; 147 | @try { 148 | a2 = [a1 mapUsingSelector:@selector(stringByReplacingCharactersInRange:withString:), NSMakeRange(0, 1), @"*", nil]; 149 | } @catch (NSException *e) { 150 | exceptionWasRaised = YES; 151 | } 152 | STAssertEquals(YES, exceptionWasRaised, @""); 153 | } 154 | 155 | @end 156 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Map, filter and reduce in Objective-C/Cocoa 2 | ------------------------------------------- 3 | 4 | This is a category to add Python like map, filter and reduce methods to Cocoa NSArray: 5 | 6 | @interface NSArray (Functional) 7 | 8 | - (NSArray *)filterUsingSelector:(SEL)aSelector, ...; 9 | - (NSArray *)mapUsingSelector:(SEL)aSelector, ...; 10 | - (id)reduceUsingSelector:(SEL)aSelector; 11 | 12 | @end 13 | 14 | You can then program in functional style: 15 | 16 | NSArray *a = [NSArray arrayWithObjects:@"a", @"ab", @"abc", @"bc", @"c", nil]; 17 | 18 | NSArray *x = [a filterUsingSelector:@selector(hasPrefix:), @"a", nil]; 19 | NSArray *y = [a mapUsingSelector:@selector(uppercaseString), nil]; 20 | NSArray *z = [a reduceUsingSelector:@selector(stringByAppendingString:)]; 21 | 22 | Results: 23 | 24 | x: (a, ab, abc) 25 | y: (A, AB, ABC, BC, C) 26 | z: aababcbcc 27 | 28 | --- 29 | 30 | #### Functional style programming with Cocoa NSArray 31 | 32 | When programming in Objective-C, I often miss functions like `map()`, `filter()` and `reduce()`, as they exist in Python. 33 | 34 | When you think about it, there is no need to wait the upcoming "blocks" in Snow Leopard. It is already possible to do this on Mac OS X 10.5, 10.4 and probably below, thanks to [NSInvocation](http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/nsinvocation_Class/Reference/Reference.html) and variable length arguments lists. 35 | 36 | Here is a neat NSArray category to do this properly: [nsarray-functional](http://github.com/nst/nsarray-functional). 37 | 38 | Your [comments](http://seriot.ch/contact.php) are welcome. 39 | 40 | #### Functional programming in Python 41 | 42 | In Python, `map()`, `filter()` and `reduce()` are normally used with lambda functions, ie anonymous functions: 43 | 44 | >>> l = ['a', 'ab', 'abc', 'bc', 'c'] 45 | 46 | >>> filter(lambda x:x.startswith('a'), l) 47 | ['a', 'ab', 'abc'] 48 | 49 | >>> map(lambda x:x.upper(), l) 50 | ['A', 'AB', 'ABC', 'BC', 'C'] 51 | 52 | >>> reduce(lambda x,y:x+y, l) 53 | aababcbcc 54 | 55 | #### When Objective-C will have blocks 56 | 57 | Apple has added a new syntax to C allowing to write anonymous functions with "blocks". Blocks will be available in Snow Leopard and allow passing blocks as arguments to methods, so that we can easily implement and call `-[NSArray map:]` and `-[NSArray filter:]` methods. 58 | 59 | l = [l map:^(id obj){ return [obj uppercaseString]; }]; 60 | l = [l filter:^(id obj){ return [obj hasPrefix:@"a"]; }]; 61 | 62 | #### NSArray filtering with NSPredicate 63 | 64 | [NSPredicate can already filter](http://developer.apple.com/documentation/Cocoa/Conceptual/Predicates/predicates.html) an [NSArray](http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSArray_Class/Reference/Reference.html), using [Key-Value Coding](http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueCoding/KeyValueCoding.html): 65 | 66 | NSPredicate *p = [NSPredicate predicateWithFormat: @"SELF beginswith 'a'"]; 67 | NSLog(@"-- p: %@", [a filteredArrayUsingPredicate:p]); 68 | 69 | Unfortunately, NSPredicate is not part of iPhone SDK. 70 | 71 | #### NSArray "mapping" with makeObjectsPerformSelector: 72 | 73 | The following methods exists on NSArray: 74 | 75 | -(void)makeObjectsPerformSelector:(SEL)aSelector; 76 | -(void)makeObjectsPerformSelector:(SEL)aSelector withObject:(id)anObject; 77 | 78 | Unfortunately, this is not mapping as we mean it in Python, because: 79 | 80 | - it doesn't return anything 81 | - it should not modify the receiver 82 | - it prevents using selectors with primitive type argument or several arguments 83 | 84 | #### A 'Functional' category to NSArray 85 | 86 | The following category provides filter, map and reduce to NSArray: 87 | 88 | @interface NSArray (Functional) 89 | - (NSArray *)filterUsingSelector:(SEL)aSelector, ...; // selector returning BOOL 90 | - (NSArray *)mapUsingSelector:(SEL)aSelector, ...; // selector returning id 91 | - (id)reduceUsingSelector:(SEL)aSelector; // selector returning id 92 | @end 93 | 94 | You can then program in functional style: 95 | 96 | NSArray *a = [NSArray arrayWithObjects:@"a", @"ab", @"abc", @"bc", @"c", nil]; 97 | 98 | NSArray *x = [a filterUsingSelector:@selector(hasPrefix:), @"a", nil]; 99 | NSArray *y = [a mapUsingSelector:@selector(uppercaseString), nil]; 100 | id z = [a reduceUsingSelector:@selector(stringByAppendingString:)]; 101 | 102 | Results: 103 | 104 | x: (a, ab, abc) 105 | y: (A, AB, ABC, BC, C) 106 | z: aababcbcc 107 | 108 | We can also perform custom selectors, by implementing them in categories on the classes of the objects that are in the array. Notice that we can use privitive types arguments. 109 | 110 | NSArray *f = [a filterUsingSelector:@selector(isLongerThan:), 1, nil]); 111 | 112 | f: (ab, abc, ab) 113 | 114 | Purists are free to rename these methods according to Cocoa conventions: 115 | 116 | -arrayByFilteringArrayUsingSelector: 117 | -arrayByMappingArrayUsingSelector: 118 | -objectByReducingArrayUsingSelector: 119 | 120 | #### Implementation 121 | 122 | NSArray+Functional does not need any NSProxy subclass. No trampoline object needed. Basically, `filterUsingSelector:` and `mapUsingSelector:` will fill an NSMutableArray according to the result of the invocation of the selector with its parameters on each object in the array, when `reduceUsingSelector:` is trivial. 123 | 124 | #### Trampolines 125 | 126 | Another way to achieve this so-called [High Order Messaging](http://cocoadev.com/index.pl?HigherOrderMessaging) is using sophisticated trampolines that allow writing beautiful code like this: 127 | 128 | NSArray *a = [[array select] hasPrefix:@"a"]; // filter 129 | NSArray *b = [[array reject] hasSuffix:@"z"]; 130 | NSArray *c = [[array collect] stringByAppendingString:@"_"]; // map 131 | 132 | Still I have yet to find a simple implementation that I like and that does not use private methods. The last thing I want is a relying on classes which can break at any time. 133 | 134 | _[2009-01-17] Marcel Weiher answered to my complain by publishing a short, clean and clear HOM implementation: [Simple HOM](http://www.metaobject.com/blog/2009/01/simple-hom.html)._ 135 | 136 | #### A word on Python 3.0 137 | 138 | Python 3.0 dropped map(), filter() and reduce(). Guido van Rossum [explains](http://www.artima.com/weblogs/viewpost.jsp?thread=98196) why he prefers the new syntax: 139 | 140 | filter(lambda x:x.startswith('a'), l) # old 141 | [x for x in l if x.startswith('a')] # new 142 | -------------------------------------------------------------------------------- /Functional.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 45; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 03296DE10F1599F30081BCBA /* NSArray+Functional.m in Sources */ = {isa = PBXBuildFile; fileRef = 03296DE00F1599F30081BCBA /* NSArray+Functional.m */; }; 11 | 032976950F169CC10081BCBA /* NSInvocation+Functional.m in Sources */ = {isa = PBXBuildFile; fileRef = 032976940F169CC10081BCBA /* NSInvocation+Functional.m */; }; 12 | 032D03560F2217E00085AF46 /* UnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 032D03550F2217E00085AF46 /* UnitTests.m */; }; 13 | 032D035F0F2219080085AF46 /* NSArray+Functional.m in Sources */ = {isa = PBXBuildFile; fileRef = 03296DE00F1599F30081BCBA /* NSArray+Functional.m */; }; 14 | 032D03600F22190A0085AF46 /* NSString+Cat.m in Sources */ = {isa = PBXBuildFile; fileRef = 036079D50F17854100A168E4 /* NSString+Cat.m */; }; 15 | 032D03610F22190B0085AF46 /* NSInvocation+Functional.m in Sources */ = {isa = PBXBuildFile; fileRef = 032976940F169CC10081BCBA /* NSInvocation+Functional.m */; }; 16 | 036079D60F17854100A168E4 /* NSString+Cat.m in Sources */ = {isa = PBXBuildFile; fileRef = 036079D50F17854100A168E4 /* NSString+Cat.m */; }; 17 | 8DD76F9A0486AA7600D96B5E /* Functional.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* Functional.m */; settings = {ATTRIBUTES = (); }; }; 18 | 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; }; 19 | 8DD76F9F0486AA7600D96B5E /* Functional.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C6859EA3029092ED04C91782 /* Functional.1 */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXContainerItemProxy section */ 23 | 032D05B80F2251600085AF46 /* PBXContainerItemProxy */ = { 24 | isa = PBXContainerItemProxy; 25 | containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; 26 | proxyType = 1; 27 | remoteGlobalIDString = 032D034E0F2217CA0085AF46; 28 | remoteInfo = UnitTests; 29 | }; 30 | /* End PBXContainerItemProxy section */ 31 | 32 | /* Begin PBXCopyFilesBuildPhase section */ 33 | 8DD76F9E0486AA7600D96B5E /* CopyFiles */ = { 34 | isa = PBXCopyFilesBuildPhase; 35 | buildActionMask = 8; 36 | dstPath = /usr/share/man/man1/; 37 | dstSubfolderSpec = 0; 38 | files = ( 39 | 8DD76F9F0486AA7600D96B5E /* Functional.1 in CopyFiles */, 40 | ); 41 | runOnlyForDeploymentPostprocessing = 1; 42 | }; 43 | /* End PBXCopyFilesBuildPhase section */ 44 | 45 | /* Begin PBXFileReference section */ 46 | 03296DDF0F1599F30081BCBA /* NSArray+Functional.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Functional.h"; sourceTree = ""; }; 47 | 03296DE00F1599F30081BCBA /* NSArray+Functional.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Functional.m"; sourceTree = ""; }; 48 | 032976930F169CC10081BCBA /* NSInvocation+Functional.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSInvocation+Functional.h"; sourceTree = ""; }; 49 | 032976940F169CC10081BCBA /* NSInvocation+Functional.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSInvocation+Functional.m"; sourceTree = ""; }; 50 | 032D034F0F2217CA0085AF46 /* UnitTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 032D03500F2217CA0085AF46 /* UnitTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "UnitTests-Info.plist"; sourceTree = ""; }; 52 | 032D03540F2217E00085AF46 /* UnitTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UnitTests.h; sourceTree = ""; }; 53 | 032D03550F2217E00085AF46 /* UnitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UnitTests.m; sourceTree = ""; }; 54 | 036079D40F17854100A168E4 /* NSString+Cat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Cat.h"; sourceTree = ""; }; 55 | 036079D50F17854100A168E4 /* NSString+Cat.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Cat.m"; sourceTree = ""; }; 56 | 08FB7796FE84155DC02AAC07 /* Functional.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Functional.m; sourceTree = ""; }; 57 | 08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 58 | 32A70AAB03705E1F00C91783 /* Functional_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Functional_Prefix.pch; sourceTree = ""; }; 59 | 8DD76FA10486AA7600D96B5E /* Functional */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Functional; sourceTree = BUILT_PRODUCTS_DIR; }; 60 | C6859EA3029092ED04C91782 /* Functional.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = Functional.1; sourceTree = ""; }; 61 | /* End PBXFileReference section */ 62 | 63 | /* Begin PBXFrameworksBuildPhase section */ 64 | 032D034C0F2217CA0085AF46 /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | 8DD76F9B0486AA7600D96B5E /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */, 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | /* End PBXFrameworksBuildPhase section */ 80 | 81 | /* Begin PBXGroup section */ 82 | 032D03590F2217EA0085AF46 /* UnitTests */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 032D03540F2217E00085AF46 /* UnitTests.h */, 86 | 032D03550F2217E00085AF46 /* UnitTests.m */, 87 | ); 88 | name = UnitTests; 89 | sourceTree = ""; 90 | }; 91 | 08FB7794FE84155DC02AAC07 /* Functional */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 32A70AAB03705E1F00C91783 /* Functional_Prefix.pch */, 95 | 08FB7795FE84155DC02AAC07 /* Source */, 96 | 032D03590F2217EA0085AF46 /* UnitTests */, 97 | C6859EA2029092E104C91782 /* Documentation */, 98 | 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */, 99 | 1AB674ADFE9D54B511CA2CBB /* Products */, 100 | 032D03500F2217CA0085AF46 /* UnitTests-Info.plist */, 101 | ); 102 | name = Functional; 103 | sourceTree = ""; 104 | }; 105 | 08FB7795FE84155DC02AAC07 /* Source */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 032976930F169CC10081BCBA /* NSInvocation+Functional.h */, 109 | 032976940F169CC10081BCBA /* NSInvocation+Functional.m */, 110 | 03296DDF0F1599F30081BCBA /* NSArray+Functional.h */, 111 | 03296DE00F1599F30081BCBA /* NSArray+Functional.m */, 112 | 036079D40F17854100A168E4 /* NSString+Cat.h */, 113 | 036079D50F17854100A168E4 /* NSString+Cat.m */, 114 | 08FB7796FE84155DC02AAC07 /* Functional.m */, 115 | ); 116 | name = Source; 117 | sourceTree = ""; 118 | }; 119 | 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 08FB779EFE84155DC02AAC07 /* Foundation.framework */, 123 | ); 124 | name = "External Frameworks and Libraries"; 125 | sourceTree = ""; 126 | }; 127 | 1AB674ADFE9D54B511CA2CBB /* Products */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 8DD76FA10486AA7600D96B5E /* Functional */, 131 | 032D034F0F2217CA0085AF46 /* UnitTests.octest */, 132 | ); 133 | name = Products; 134 | sourceTree = ""; 135 | }; 136 | C6859EA2029092E104C91782 /* Documentation */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | C6859EA3029092ED04C91782 /* Functional.1 */, 140 | ); 141 | name = Documentation; 142 | sourceTree = ""; 143 | }; 144 | /* End PBXGroup section */ 145 | 146 | /* Begin PBXNativeTarget section */ 147 | 032D034E0F2217CA0085AF46 /* UnitTests */ = { 148 | isa = PBXNativeTarget; 149 | buildConfigurationList = 032D03530F2217CB0085AF46 /* Build configuration list for PBXNativeTarget "UnitTests" */; 150 | buildPhases = ( 151 | 032D034A0F2217CA0085AF46 /* Resources */, 152 | 032D034B0F2217CA0085AF46 /* Sources */, 153 | 032D034C0F2217CA0085AF46 /* Frameworks */, 154 | 032D034D0F2217CA0085AF46 /* ShellScript */, 155 | ); 156 | buildRules = ( 157 | ); 158 | dependencies = ( 159 | ); 160 | name = UnitTests; 161 | productName = UnitTests; 162 | productReference = 032D034F0F2217CA0085AF46 /* UnitTests.octest */; 163 | productType = "com.apple.product-type.bundle"; 164 | }; 165 | 8DD76F960486AA7600D96B5E /* Functional */ = { 166 | isa = PBXNativeTarget; 167 | buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "Functional" */; 168 | buildPhases = ( 169 | 8DD76F990486AA7600D96B5E /* Sources */, 170 | 8DD76F9B0486AA7600D96B5E /* Frameworks */, 171 | 8DD76F9E0486AA7600D96B5E /* CopyFiles */, 172 | ); 173 | buildRules = ( 174 | ); 175 | dependencies = ( 176 | 032D05B90F2251600085AF46 /* PBXTargetDependency */, 177 | ); 178 | name = Functional; 179 | productInstallPath = "$(HOME)/bin"; 180 | productName = Functional; 181 | productReference = 8DD76FA10486AA7600D96B5E /* Functional */; 182 | productType = "com.apple.product-type.tool"; 183 | }; 184 | /* End PBXNativeTarget section */ 185 | 186 | /* Begin PBXProject section */ 187 | 08FB7793FE84155DC02AAC07 /* Project object */ = { 188 | isa = PBXProject; 189 | buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "Functional" */; 190 | compatibilityVersion = "Xcode 3.1"; 191 | hasScannedForEncodings = 1; 192 | mainGroup = 08FB7794FE84155DC02AAC07 /* Functional */; 193 | projectDirPath = ""; 194 | projectRoot = ""; 195 | targets = ( 196 | 8DD76F960486AA7600D96B5E /* Functional */, 197 | 032D034E0F2217CA0085AF46 /* UnitTests */, 198 | ); 199 | }; 200 | /* End PBXProject section */ 201 | 202 | /* Begin PBXResourcesBuildPhase section */ 203 | 032D034A0F2217CA0085AF46 /* Resources */ = { 204 | isa = PBXResourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | ); 208 | runOnlyForDeploymentPostprocessing = 0; 209 | }; 210 | /* End PBXResourcesBuildPhase section */ 211 | 212 | /* Begin PBXShellScriptBuildPhase section */ 213 | 032D034D0F2217CA0085AF46 /* ShellScript */ = { 214 | isa = PBXShellScriptBuildPhase; 215 | buildActionMask = 2147483647; 216 | files = ( 217 | ); 218 | inputPaths = ( 219 | ); 220 | outputPaths = ( 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | shellPath = /bin/sh; 224 | shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; 225 | }; 226 | /* End PBXShellScriptBuildPhase section */ 227 | 228 | /* Begin PBXSourcesBuildPhase section */ 229 | 032D034B0F2217CA0085AF46 /* Sources */ = { 230 | isa = PBXSourcesBuildPhase; 231 | buildActionMask = 2147483647; 232 | files = ( 233 | 032D03560F2217E00085AF46 /* UnitTests.m in Sources */, 234 | 032D035F0F2219080085AF46 /* NSArray+Functional.m in Sources */, 235 | 032D03600F22190A0085AF46 /* NSString+Cat.m in Sources */, 236 | 032D03610F22190B0085AF46 /* NSInvocation+Functional.m in Sources */, 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | 8DD76F990486AA7600D96B5E /* Sources */ = { 241 | isa = PBXSourcesBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | 8DD76F9A0486AA7600D96B5E /* Functional.m in Sources */, 245 | 03296DE10F1599F30081BCBA /* NSArray+Functional.m in Sources */, 246 | 032976950F169CC10081BCBA /* NSInvocation+Functional.m in Sources */, 247 | 036079D60F17854100A168E4 /* NSString+Cat.m in Sources */, 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | }; 251 | /* End PBXSourcesBuildPhase section */ 252 | 253 | /* Begin PBXTargetDependency section */ 254 | 032D05B90F2251600085AF46 /* PBXTargetDependency */ = { 255 | isa = PBXTargetDependency; 256 | target = 032D034E0F2217CA0085AF46 /* UnitTests */; 257 | targetProxy = 032D05B80F2251600085AF46 /* PBXContainerItemProxy */; 258 | }; 259 | /* End PBXTargetDependency section */ 260 | 261 | /* Begin XCBuildConfiguration section */ 262 | 032D03510F2217CB0085AF46 /* Debug */ = { 263 | isa = XCBuildConfiguration; 264 | buildSettings = { 265 | ALWAYS_SEARCH_USER_PATHS = NO; 266 | COPY_PHASE_STRIP = NO; 267 | FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; 268 | GCC_DYNAMIC_NO_PIC = NO; 269 | GCC_ENABLE_FIX_AND_CONTINUE = NO; 270 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 271 | GCC_MODEL_TUNING = G5; 272 | GCC_OPTIMIZATION_LEVEL = 0; 273 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 274 | GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Cocoa.framework/Headers/Cocoa.h"; 275 | INFOPLIST_FILE = "UnitTests-Info.plist"; 276 | INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles"; 277 | OTHER_LDFLAGS = ( 278 | "-framework", 279 | Cocoa, 280 | "-framework", 281 | SenTestingKit, 282 | ); 283 | PREBINDING = NO; 284 | PRODUCT_NAME = UnitTests; 285 | WRAPPER_EXTENSION = octest; 286 | }; 287 | name = Debug; 288 | }; 289 | 032D03520F2217CB0085AF46 /* Release */ = { 290 | isa = XCBuildConfiguration; 291 | buildSettings = { 292 | ALWAYS_SEARCH_USER_PATHS = NO; 293 | COPY_PHASE_STRIP = YES; 294 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 295 | FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; 296 | GCC_ENABLE_FIX_AND_CONTINUE = NO; 297 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 298 | GCC_MODEL_TUNING = G5; 299 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 300 | GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Cocoa.framework/Headers/Cocoa.h"; 301 | INFOPLIST_FILE = "UnitTests-Info.plist"; 302 | INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles"; 303 | OTHER_LDFLAGS = ( 304 | "-framework", 305 | Cocoa, 306 | "-framework", 307 | SenTestingKit, 308 | ); 309 | PREBINDING = NO; 310 | PRODUCT_NAME = UnitTests; 311 | WRAPPER_EXTENSION = octest; 312 | ZERO_LINK = NO; 313 | }; 314 | name = Release; 315 | }; 316 | 1DEB927508733DD40010E9CD /* Debug */ = { 317 | isa = XCBuildConfiguration; 318 | buildSettings = { 319 | ALWAYS_SEARCH_USER_PATHS = NO; 320 | COPY_PHASE_STRIP = NO; 321 | GCC_DYNAMIC_NO_PIC = NO; 322 | GCC_ENABLE_FIX_AND_CONTINUE = YES; 323 | GCC_MODEL_TUNING = G5; 324 | GCC_OPTIMIZATION_LEVEL = 0; 325 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 326 | GCC_PREFIX_HEADER = Functional_Prefix.pch; 327 | INSTALL_PATH = /usr/local/bin; 328 | PRODUCT_NAME = Functional; 329 | }; 330 | name = Debug; 331 | }; 332 | 1DEB927608733DD40010E9CD /* Release */ = { 333 | isa = XCBuildConfiguration; 334 | buildSettings = { 335 | ALWAYS_SEARCH_USER_PATHS = NO; 336 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 337 | GCC_MODEL_TUNING = G5; 338 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 339 | GCC_PREFIX_HEADER = Functional_Prefix.pch; 340 | INSTALL_PATH = /usr/local/bin; 341 | PRODUCT_NAME = Functional; 342 | }; 343 | name = Release; 344 | }; 345 | 1DEB927908733DD40010E9CD /* Debug */ = { 346 | isa = XCBuildConfiguration; 347 | buildSettings = { 348 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 349 | GCC_C_LANGUAGE_STANDARD = c99; 350 | GCC_OPTIMIZATION_LEVEL = 0; 351 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 352 | GCC_WARN_UNUSED_VARIABLE = YES; 353 | ONLY_ACTIVE_ARCH = YES; 354 | PREBINDING = NO; 355 | SDKROOT = macosx10.5; 356 | }; 357 | name = Debug; 358 | }; 359 | 1DEB927A08733DD40010E9CD /* Release */ = { 360 | isa = XCBuildConfiguration; 361 | buildSettings = { 362 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 363 | GCC_C_LANGUAGE_STANDARD = c99; 364 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 365 | GCC_WARN_UNUSED_VARIABLE = YES; 366 | PREBINDING = NO; 367 | SDKROOT = macosx10.5; 368 | }; 369 | name = Release; 370 | }; 371 | /* End XCBuildConfiguration section */ 372 | 373 | /* Begin XCConfigurationList section */ 374 | 032D03530F2217CB0085AF46 /* Build configuration list for PBXNativeTarget "UnitTests" */ = { 375 | isa = XCConfigurationList; 376 | buildConfigurations = ( 377 | 032D03510F2217CB0085AF46 /* Debug */, 378 | 032D03520F2217CB0085AF46 /* Release */, 379 | ); 380 | defaultConfigurationIsVisible = 0; 381 | defaultConfigurationName = Release; 382 | }; 383 | 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "Functional" */ = { 384 | isa = XCConfigurationList; 385 | buildConfigurations = ( 386 | 1DEB927508733DD40010E9CD /* Debug */, 387 | 1DEB927608733DD40010E9CD /* Release */, 388 | ); 389 | defaultConfigurationIsVisible = 0; 390 | defaultConfigurationName = Release; 391 | }; 392 | 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "Functional" */ = { 393 | isa = XCConfigurationList; 394 | buildConfigurations = ( 395 | 1DEB927908733DD40010E9CD /* Debug */, 396 | 1DEB927A08733DD40010E9CD /* Release */, 397 | ); 398 | defaultConfigurationIsVisible = 0; 399 | defaultConfigurationName = Release; 400 | }; 401 | /* End XCConfigurationList section */ 402 | }; 403 | rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; 404 | } 405 | --------------------------------------------------------------------------------